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 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
|
||||
|
||||
- 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.
|
||||
- `.classpath` targets `JavaSE-25`. Sources contain Chinese text, so manual compilation must use UTF-8:
|
||||
- This is an Eclipse Java project, not Maven or Gradle: `src/` contains sources, `lib/` vendored dependencies, and `bin/` compiled output. `.classpath` targets Java 25.
|
||||
- Source files contain Chinese text, so compile with UTF-8 from the repository root:
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
- 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
|
||||
& "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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
## Dashboard
|
||||
|
||||
- `dashboard/` is a Git submodule. Commit dashboard changes inside it, then update the parent repository's submodule pointer.
|
||||
- Run frontend commands from `dashboard/` with pnpm:
|
||||
|
||||
```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.
|
||||
- `dashboard/` is a Git submodule. Commit dashboard changes in that repository, then update the parent submodule pointer.
|
||||
- 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.
|
||||
|
||||
## Verification
|
||||
|
||||
- There is no CI or automated test suite. `*Test*` classes are manual harnesses that require real network peers.
|
||||
- For Java changes, compile and launch the app. For dashboard changes, run `pnpm typecheck` and `pnpm build`.
|
||||
- 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`.
|
||||
|
||||
## 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.
|
||||
- `/api/config` is field-by-field parsing, not whole-object Gson mapping. Keep legacy key aliases in sync with new fields.
|
||||
- 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()`.
|
||||
- `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` 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: 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`.
|
||||
- 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 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` 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.
|
||||
- `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.
|
||||
- `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.
|
||||
|
||||
+1
-1
Submodule dashboard updated: 0b705f9ba0...8c13af680a
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,129 +1,208 @@
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.congestion.NOCongestionAlgorithm;
|
||||
import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
|
||||
import org.kne.opencl64.Releaser;
|
||||
|
||||
public class KLALBRoutingProtocolAPIClient {
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
private SendPacketSlidingWindow<UUID, JsonDataPacket> window = new SendPacketSlidingWindow<UUID, JsonDataPacket>(
|
||||
new NOCongestionAlgorithm(3000000000L), 1024 * 1024);
|
||||
|
||||
private static final Cleaner clr = Cleaner.create();
|
||||
|
||||
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
||||
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
||||
InetSocketAddress addrs = (InetSocketAddress) addr;
|
||||
UUID ruid = dataobj.getUuid();
|
||||
JsonDataPacket relate = null;
|
||||
//System.out.println(ruid + " " + window.getSendmap());
|
||||
switch (dataobj.getType()) {
|
||||
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP:
|
||||
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP:
|
||||
|
||||
if ((relate = window.ack(ruid)) != null) {
|
||||
List<?> connects = (List<?>) dataobj.getData();
|
||||
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.congestion.NOCongestionAlgorithm;
|
||||
import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
|
||||
import org.kne.cloud.network.klalb.SendItem;
|
||||
import org.kne.opencl64.Releaser;
|
||||
|
||||
public class KLALBRoutingProtocolAPIClient {
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
private SendPacketSlidingWindow<UUID, JsonDataPacket> window = new SendPacketSlidingWindow<UUID, JsonDataPacket>(
|
||||
new NOCongestionAlgorithm(3000000000L), 1024 * 1024);
|
||||
|
||||
private static final Cleaner clr = Cleaner.create();
|
||||
|
||||
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
||||
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
||||
UUID ruid = dataobj.getUuid();
|
||||
switch (dataobj.getType()) {
|
||||
case KLALBRoutingProtocolJsonData.NODE_PROFILE_RESP:
|
||||
PendingRequest profileRequest = acknowledgeResponse(ruid,
|
||||
KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ, addr);
|
||||
if (profileRequest != null) {
|
||||
((Consumer<NodeProfile>) profileRequest.callback)
|
||||
.accept(new NodeProfile(dataobj.getDeviceName(), dataobj.getDeviceDescription()));
|
||||
}
|
||||
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;
|
||||
case KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_RESP:
|
||||
PendingRequest routesRequest = acknowledgeResponse(ruid,
|
||||
KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ, addr);
|
||||
if (routesRequest != null) {
|
||||
((Consumer<ExtraRoutesResult>) routesRequest.callback).accept(
|
||||
new ExtraRoutesResult(decodeExtraRoutes(dataobj.getData()), dataobj.getStatus()));
|
||||
}
|
||||
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.
|
||||
}
|
||||
List<String> extraRoutes = dataobj.getExtraRoutes();
|
||||
if(extraRoutes == null) {
|
||||
extraRoutes = new ArrayList<String>();
|
||||
}
|
||||
// 组装节点信息(线路 + 设备名称 + 设备描述,精简模式下线路与描述为 null)
|
||||
KLALBNodeInformation info = new KLALBNodeInformation(connectsm, dataobj.getDeviceName(),
|
||||
dataobj.getDeviceDescription(), extraRoutes);
|
||||
((Consumer<KLALBNodeInformation>) relate.getUserCallback()).accept(info);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public KLALBRoutingProtocolAPIClient(KLALBRoutingProtocol routingProtocol) {
|
||||
this.routingProtocol = routingProtocol;
|
||||
routingProtocol.addReceiver(rec);
|
||||
this.releaser = new KLALBRoutingProtocolAPIClientReleaser(this.routingProtocol, rec, window);
|
||||
clr.register(this, releaser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 精简查询:仅获取对端设备名称(开销最小,适用于未查看节点详情的场景)。
|
||||
*/
|
||||
public void requestNodeInfoTiny(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
||||
throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ, addr, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整查询:获取对端开放线路 + 设备名称 + 设备描述(查看节点信息时使用)。
|
||||
*/
|
||||
public void requestNodeInfoFull(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
||||
throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ, addr, callback);
|
||||
}
|
||||
|
||||
private void sendNodeInfoRequest(String type, SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
||||
throws IOException {
|
||||
UUID suid = UUID.randomUUID();
|
||||
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
|
||||
JsonDataPacket packet = new JsonDataPacket(json);
|
||||
packet.setUserCallback(callback);
|
||||
window.put(suid, packet);
|
||||
routingProtocol.sendJsonPacketToAddress(packet, addr);
|
||||
|
||||
}
|
||||
|
||||
public KLALBRoutingProtocol getRoutingProtocol() {
|
||||
return routingProtocol;
|
||||
}
|
||||
|
||||
private KLALBRoutingProtocolAPIClientReleaser releaser;
|
||||
|
||||
public void close() {
|
||||
releaser.run();
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return releaser.isReleased();
|
||||
}
|
||||
}
|
||||
|
||||
class KLALBRoutingProtocolAPIClientReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
|
||||
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
private SendPacketSlidingWindow<UUID, JsonDataPacket> window;
|
||||
|
||||
public KLALBRoutingProtocolAPIClientReleaser(KLALBRoutingProtocol routingProtocol,
|
||||
BiConsumer<SocketAddress, JsonDataPacket> resource, SendPacketSlidingWindow<UUID, JsonDataPacket> window) {
|
||||
super(resource);
|
||||
this.routingProtocol = routingProtocol;
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||
window.close();
|
||||
routingProtocol.removeReceiver(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
this.routingProtocol = routingProtocol;
|
||||
routingProtocol.addReceiver(rec);
|
||||
this.releaser = new KLALBRoutingProtocolAPIClientReleaser(this.routingProtocol, rec, window);
|
||||
clr.register(this, releaser);
|
||||
}
|
||||
|
||||
public void requestNodeProfile(SocketAddress addr, Consumer<NodeProfile> callback) throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ, addr, callback);
|
||||
}
|
||||
|
||||
public void requestExternalEndpoints(SocketAddress addr, Consumer<ExternalEndpointsResult> callback)
|
||||
throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ, addr, callback);
|
||||
}
|
||||
|
||||
public void requestExtraRoutes(SocketAddress addr, Consumer<ExtraRoutesResult> callback) 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();
|
||||
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
|
||||
JsonDataPacket packet = new JsonDataPacket(json);
|
||||
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);
|
||||
routingProtocol.sendJsonPacketToAddress(packet, addr);
|
||||
}
|
||||
}
|
||||
|
||||
public KLALBRoutingProtocol getRoutingProtocol() {
|
||||
return routingProtocol;
|
||||
}
|
||||
|
||||
private KLALBRoutingProtocolAPIClientReleaser releaser;
|
||||
|
||||
public synchronized void close() {
|
||||
if (closed.compareAndSet(false, true)) releaser.run();
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
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>> {
|
||||
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
private SendPacketSlidingWindow<UUID, JsonDataPacket> window;
|
||||
|
||||
public KLALBRoutingProtocolAPIClientReleaser(KLALBRoutingProtocol routingProtocol,
|
||||
BiConsumer<SocketAddress, JsonDataPacket> resource, SendPacketSlidingWindow<UUID, JsonDataPacket> window) {
|
||||
super(resource);
|
||||
this.routingProtocol = routingProtocol;
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||
window.close();
|
||||
routingProtocol.removeReceiver(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +1,98 @@
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.net.InetSocketAddress;
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
import org.kne.opencl64.Releaser;
|
||||
|
||||
public class KLALBRoutingProtocolAPIServer {
|
||||
private KLALBController controller;
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
|
||||
|
||||
private static final Cleaner clr=Cleaner.create();
|
||||
|
||||
private BiConsumer<SocketAddress, JsonDataPacket> rec=(addr,data)->{
|
||||
try {
|
||||
KLALBRoutingProtocolJsonData dataobj= data.getDecodedData();
|
||||
InetSocketAddress addrs=(InetSocketAddress) addr;
|
||||
switch(dataobj.getType()){
|
||||
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ:
|
||||
// 精简查询:仅返回设备名称(设备名称随路由信息广播公开,不受 denyExternalEndpointQuery 限制)
|
||||
KLALBController.PublishedNodeInfo published = controller.getPublishedNodeInfo();
|
||||
KLALBRoutingProtocolJsonData tinyjson=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP,dataobj.getUuid(),null);
|
||||
tinyjson.setDeviceName(published.getDeviceName());
|
||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(tinyjson),addr);
|
||||
break;
|
||||
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ:
|
||||
// 完整查询:设备名称与描述总是正常响应;denyExternalEndpointQuery 仅隐藏外部端点列表
|
||||
boolean denyEndpoints=controller.getConfigItem()!=null&&controller.getConfigItem().isDenyExternalEndpointQuery();
|
||||
KLALBController.PublishedNodeInfo fullPublished = controller.getPublishedNodeInfo();
|
||||
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP,dataobj.getUuid(),denyEndpoints?null:new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>(fullPublished.getExternalEndpoints()));
|
||||
json.setDeviceName(fullPublished.getDeviceName());// 附带本机设备名称
|
||||
json.setDeviceDescription(fullPublished.getDeviceDescription());// 附带本机设备描述
|
||||
json.setExtraRoutes(new ArrayList<String>(fullPublished.getExtraRoutes()));
|
||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
public KLALBRoutingProtocolAPIServer(KLALBRoutingProtocol routingProtocol,KLALBController controller) {
|
||||
this.controller=controller;
|
||||
this.routingProtocol=routingProtocol;
|
||||
routingProtocol.addReceiver(rec);
|
||||
this.releaser=new KLALBRoutingProtocolAPIServerReleaser(this.routingProtocol,rec);
|
||||
clr.register(this, releaser);
|
||||
}
|
||||
|
||||
public KLALBController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public KLALBRoutingProtocol getRoutingProtocol() {
|
||||
return routingProtocol;
|
||||
}
|
||||
|
||||
|
||||
private KLALBRoutingProtocolAPIServerReleaser releaser;
|
||||
|
||||
public void close() {
|
||||
releaser.run();
|
||||
}
|
||||
|
||||
|
||||
public boolean isClosed() {
|
||||
return releaser.isReleased();
|
||||
}
|
||||
|
||||
}
|
||||
class KLALBRoutingProtocolAPIServerReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>>{
|
||||
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
|
||||
public KLALBRoutingProtocolAPIServerReleaser(KLALBRoutingProtocol routingProtocol,BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||
super(resource);
|
||||
this.routingProtocol=routingProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||
routingProtocol.removeReceiver(resource);
|
||||
}
|
||||
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
import org.kne.opencl64.Releaser;
|
||||
|
||||
public class KLALBRoutingProtocolAPIServer {
|
||||
private KLALBController controller;
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
|
||||
private static final Cleaner clr = Cleaner.create();
|
||||
|
||||
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
||||
try {
|
||||
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
||||
switch (dataobj.getType()) {
|
||||
case KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ:
|
||||
KLALBController.PublishedNodeInfo profilePublished = controller.getPublishedNodeInfo();
|
||||
KLALBRoutingProtocolJsonData profile = new KLALBRoutingProtocolJsonData(
|
||||
KLALBRoutingProtocolJsonData.NODE_PROFILE_RESP, dataobj.getUuid(), null);
|
||||
profile.setDeviceName(profilePublished.getDeviceName());
|
||||
profile.setDeviceDescription(profilePublished.getDeviceDescription());
|
||||
profile.setStatus(NodeInfoQueryStatus.OK);
|
||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(profile), addr);
|
||||
break;
|
||||
case KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ:
|
||||
boolean denyEndpoints = controller.getConfigItem() != null
|
||||
&& controller.getConfigItem().isDenyExternalEndpointQuery();
|
||||
KLALBController.PublishedNodeInfo endpointsPublished = controller.getPublishedNodeInfo();
|
||||
KLALBRoutingProtocolJsonData endpoints = new KLALBRoutingProtocolJsonData(
|
||||
KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_RESP, dataobj.getUuid(),
|
||||
denyEndpoints ? new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>()
|
||||
: new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>(
|
||||
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;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
public KLALBRoutingProtocolAPIServer(KLALBRoutingProtocol routingProtocol, KLALBController controller) {
|
||||
this.controller = controller;
|
||||
this.routingProtocol = routingProtocol;
|
||||
routingProtocol.addReceiver(rec);
|
||||
this.releaser = new KLALBRoutingProtocolAPIServerReleaser(this.routingProtocol, rec);
|
||||
clr.register(this, releaser);
|
||||
}
|
||||
|
||||
public KLALBController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public KLALBRoutingProtocol getRoutingProtocol() {
|
||||
return routingProtocol;
|
||||
}
|
||||
|
||||
private KLALBRoutingProtocolAPIServerReleaser releaser;
|
||||
|
||||
public void close() {
|
||||
releaser.run();
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return releaser.isReleased();
|
||||
}
|
||||
}
|
||||
|
||||
class KLALBRoutingProtocolAPIServerReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
|
||||
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
|
||||
public KLALBRoutingProtocolAPIServerReleaser(KLALBRoutingProtocol routingProtocol,
|
||||
BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||
super(resource);
|
||||
this.routingProtocol = routingProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||
routingProtocol.removeReceiver(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.List;
|
||||
|
||||
public class KLALBRoutingProtocolJsonData {
|
||||
public static final String NODE_INFO_TINY_REQ="nodeinfotinyreq";// 精简查询:仅设备名称
|
||||
public static final String NODE_INFO_TINY_RESP="nodeinfotinyresp";
|
||||
public static final String NODE_INFO_FULL_REQ="nodeinfofullreq";// 完整查询:开放线路+设备名称+设备描述
|
||||
public static final String NODE_INFO_FULL_RESP="nodeinfofullresp";
|
||||
private String type;
|
||||
private UUID uuid;
|
||||
private Object data;
|
||||
private String deviceName;// 对端设备名称
|
||||
private String deviceDescription;// 对端设备描述
|
||||
private List<String> extraRoutes;
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
public String getDeviceDescription() {
|
||||
return deviceDescription;
|
||||
}
|
||||
|
||||
public class KLALBRoutingProtocolJsonData {
|
||||
public static final String NODE_PROFILE_REQ = "nodeprofilereq";
|
||||
public static final String NODE_PROFILE_RESP = "nodeprofileresp";
|
||||
public static final String NODE_EXTERNAL_ENDPOINTS_REQ = "nodeexternalendpointsreq";
|
||||
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 UUID uuid;
|
||||
private Object data;
|
||||
private String deviceName;
|
||||
private String deviceDescription;
|
||||
private NodeInfoQueryStatus status;
|
||||
|
||||
public KLALBRoutingProtocolJsonData(String type, UUID uuid, Object data) {
|
||||
this.type = type;
|
||||
this.uuid = uuid;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
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 List<String> getExtraRoutes() {
|
||||
return extraRoutes;
|
||||
|
||||
public NodeInfoQueryStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
public void setExtraRoutes(List<String> extraRoutes) {
|
||||
this.extraRoutes = extraRoutes;
|
||||
|
||||
public void setStatus(NodeInfoQueryStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
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))
|
||||
|
||||
@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;
|
||||
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) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.uuid = uuid;
|
||||
this.data = data;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data + ", deviceName="
|
||||
+ deviceName + ", deviceDescription=" + deviceDescription + ", extraRoutes=" + extraRoutes + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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