From e32959e0bbc326f47c30ed7347f9a9af26813f89 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 24 Oct 2019 13:54:38 +0200 Subject: [PATCH] Add the ability to report known connected clients In order to allow from-scratch recovery, we need the ability to query all existing shards to provide the identifiers of clients they know about. This patch adds the models to support such a query as well as implementing it in cluster-admin. JIRA: CONTROLLER-1626 Change-Id: Id69aeb9021c8111dad10930620c9eaacfccd0d94 Signed-off-by: Robert Varga --- .../access/concepts/ClientIdentifier.java | 16 +++- .../cluster/access/concepts/FrontendType.java | 16 +++- .../cluster/access/concepts/MemberName.java | 6 ++ .../main/yang/odl-controller-cds-types.yang | 53 +++++++++++ .../md-sal/sal-cluster-admin-api/pom.xml | 7 ++ .../src/main/yang/cluster-admin.yang | 20 ++++ .../admin/ClusterAdminRpcService.java | 91 ++++++++++++++++++- .../datastore/admin/ShardIdentifier.java | 62 +++++++++++++ .../cluster/datastore/FrontendMetadata.java | 7 ++ .../controller/cluster/datastore/Shard.java | 17 ++++ .../datastore/messages/GetKnownClients.java | 28 ++++++ .../messages/GetKnownClientsReply.java | 32 +++++++ 12 files changed, 346 insertions(+), 9 deletions(-) create mode 100644 opendaylight/md-sal/cds-access-api/src/main/yang/odl-controller-cds-types.yang create mode 100644 opendaylight/md-sal/sal-cluster-admin-impl/src/main/java/org/opendaylight/controller/cluster/datastore/admin/ShardIdentifier.java create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetKnownClients.java create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetKnownClientsReply.java diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifier.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifier.java index e97626b7eb..c317ac31b6 100644 --- a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifier.java +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifier.java @@ -17,8 +17,11 @@ import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.cds.types.rev191024.ClientGeneration; import org.opendaylight.yangtools.concepts.WritableIdentifier; import org.opendaylight.yangtools.concepts.WritableObjects; +import org.opendaylight.yangtools.yang.common.Uint64; /** * A cluster-wide unique identifier of a frontend instance. This identifier discerns between individual incarnations @@ -63,7 +66,8 @@ public final class ClientIdentifier implements WritableIdentifier { } private static final long serialVersionUID = 1L; - private final FrontendIdentifier frontendId; + + private final @NonNull FrontendIdentifier frontendId; private final long generation; ClientIdentifier(final FrontendIdentifier frontendId, final long generation) { @@ -71,12 +75,12 @@ public final class ClientIdentifier implements WritableIdentifier { this.generation = generation; } - public static ClientIdentifier create(final FrontendIdentifier frontendId, + public static @NonNull ClientIdentifier create(final FrontendIdentifier frontendId, final long generation) { return new ClientIdentifier(frontendId, generation); } - public static ClientIdentifier readFrom(final DataInput in) throws IOException { + public static @NonNull ClientIdentifier readFrom(final DataInput in) throws IOException { final FrontendIdentifier frontendId = FrontendIdentifier.readFrom(in); return new ClientIdentifier(frontendId, WritableObjects.readLong(in)); } @@ -87,7 +91,7 @@ public final class ClientIdentifier implements WritableIdentifier { WritableObjects.writeLong(out, generation); } - public FrontendIdentifier getFrontendId() { + public @NonNull FrontendIdentifier getFrontendId() { return frontendId; } @@ -95,6 +99,10 @@ public final class ClientIdentifier implements WritableIdentifier { return generation; } + public @NonNull ClientGeneration getYangGeneration() { + return new ClientGeneration(Uint64.fromLongBits(generation)); + } + @Override public int hashCode() { return frontendId.hashCode() * 31 + Long.hashCode(generation); diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendType.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendType.java index b6a7b4b260..2a2a5b2b30 100644 --- a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendType.java +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendType.java @@ -23,6 +23,7 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import java.nio.charset.StandardCharsets; import java.util.regex.Pattern; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.concepts.Identifier; import org.opendaylight.yangtools.concepts.WritableIdentifier; @@ -71,7 +72,8 @@ public final class FrontendType implements Comparable, WritableIde private static final String SIMPLE_STRING_REGEX = "^[a-zA-Z0-9-_.*+:=,!~';]+$"; private static final Pattern SIMPLE_STRING_PATTERN = Pattern.compile(SIMPLE_STRING_REGEX); private static final long serialVersionUID = 1L; - private final String name; + + private final @NonNull String name; @SuppressFBWarnings(value = "VO_VOLATILE_REFERENCE_TO_ARRAY", justification = "The array elements are non-volatile but we don't access them.") @@ -96,14 +98,14 @@ public final class FrontendType implements Comparable, WritableIde * @return A {@link FrontendType} instance * @throws IllegalArgumentException if the string is null, empty or contains invalid characters */ - public static FrontendType forName(final String name) { + public static @NonNull FrontendType forName(final String name) { checkArgument(!Strings.isNullOrEmpty(name)); checkArgument(SIMPLE_STRING_PATTERN.matcher(name).matches(), "Supplied name %s does not patch pattern %s", name, SIMPLE_STRING_REGEX); return new FrontendType(name); } - public static FrontendType readFrom(final DataInput in) throws IOException { + public static @NonNull FrontendType readFrom(final DataInput in) throws IOException { final byte[] serialized = new byte[in.readInt()]; in.readFully(serialized); return new FrontendType(new String(serialized, StandardCharsets.UTF_8)); @@ -116,10 +118,16 @@ public final class FrontendType implements Comparable, WritableIde out.write(local); } - public String getName() { + public @NonNull String getName() { return name; } + public org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.cds.types.rev191024 + . @NonNull FrontendType toYang() { + return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.cds.types.rev191024 + .FrontendType(name); + } + @Override public int hashCode() { return name.hashCode(); diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/MemberName.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/MemberName.java index 70df622b9b..8a1123da52 100644 --- a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/MemberName.java +++ b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/MemberName.java @@ -103,6 +103,12 @@ public final class MemberName implements Comparable, WritableIdentif return name; } + public org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.cds.types.rev191024 + .MemberName toYang() { + return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.cds.types.rev191024 + .MemberName(name); + } + @Override public int hashCode() { return name.hashCode(); diff --git a/opendaylight/md-sal/cds-access-api/src/main/yang/odl-controller-cds-types.yang b/opendaylight/md-sal/cds-access-api/src/main/yang/odl-controller-cds-types.yang new file mode 100644 index 0000000000..58f3b9e055 --- /dev/null +++ b/opendaylight/md-sal/cds-access-api/src/main/yang/odl-controller-cds-types.yang @@ -0,0 +1,53 @@ +module odl-controller-cds-types { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:cds:types"; + prefix "cdst"; + + organization "The OpenDaylight Project"; + + description "Common type definitions related to clustered data store."; + + revision 2019-10-24 { + description "Initial revision."; + } + + typedef member-name { + description "Cluster member name."; + type string; + } + + typedef frontend-type { + description "Frontend type."; + type string { + pattern ""; + } + } + + typedef client-generation { + description "Client generation."; + type uint64; + } + + grouping frontend-identifier { + description "Identifier of a particular frontend."; + leaf member { + type member-name; + mandatory true; + } + + leaf type { + type frontend-type; + mandatory true; + } + } + + grouping client-identifier { + description "Identifier of a particular client."; + uses frontend-identifier; + leaf generation { + type client-generation; + mandatory true; + } + } +} + diff --git a/opendaylight/md-sal/sal-cluster-admin-api/pom.xml b/opendaylight/md-sal/sal-cluster-admin-api/pom.xml index d456c0ee62..b4e0e5776b 100644 --- a/opendaylight/md-sal/sal-cluster-admin-api/pom.xml +++ b/opendaylight/md-sal/sal-cluster-admin-api/pom.xml @@ -13,4 +13,11 @@ 1.11.0-SNAPSHOT bundle + + + org.opendaylight.controller + cds-access-api + + + diff --git a/opendaylight/md-sal/sal-cluster-admin-api/src/main/yang/cluster-admin.yang b/opendaylight/md-sal/sal-cluster-admin-api/src/main/yang/cluster-admin.yang index bb0eed38b2..109845c86f 100644 --- a/opendaylight/md-sal/sal-cluster-admin-api/src/main/yang/cluster-admin.yang +++ b/opendaylight/md-sal/sal-cluster-admin-api/src/main/yang/cluster-admin.yang @@ -10,6 +10,8 @@ module cluster-admin { description "Initial revision."; } + import odl-controller-cds-types { prefix cds; } + typedef data-store-type { type enumeration { enum config { @@ -299,4 +301,22 @@ module cluster-admin { description "Returns the current role for the requested module shard."; } + + rpc get-known-clients-for-all-shards { + description "Request all shards to report their known frontend clients. This is useful for determining what + generation should a resurrected member node should use."; + + output { + uses shard-result-output { + augment shard-result { + list known-clients { + when "../succeeded = true"; + + uses cds:client-identifier; + key "member type"; + } + } + } + } + } } diff --git a/opendaylight/md-sal/sal-cluster-admin-impl/src/main/java/org/opendaylight/controller/cluster/datastore/admin/ClusterAdminRpcService.java b/opendaylight/md-sal/sal-cluster-admin-impl/src/main/java/org/opendaylight/controller/cluster/datastore/admin/ClusterAdminRpcService.java index 7e5c798e82..3e14a37645 100644 --- a/opendaylight/md-sal/sal-cluster-admin-impl/src/main/java/org/opendaylight/controller/cluster/datastore/admin/ClusterAdminRpcService.java +++ b/opendaylight/md-sal/sal-cluster-admin-impl/src/main/java/org/opendaylight/controller/cluster/datastore/admin/ClusterAdminRpcService.java @@ -15,6 +15,7 @@ import akka.pattern.Patterns; import akka.util.Timeout; import com.google.common.base.Strings; import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -30,8 +31,10 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import java.util.stream.Collectors; import org.apache.commons.lang3.SerializationUtils; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.controller.cluster.access.concepts.MemberName; @@ -40,6 +43,8 @@ import org.opendaylight.controller.cluster.datastore.messages.AddPrefixShardRepl import org.opendaylight.controller.cluster.datastore.messages.AddShardReplica; import org.opendaylight.controller.cluster.datastore.messages.ChangeShardMembersVotingStatus; import org.opendaylight.controller.cluster.datastore.messages.FlipShardMembersVotingStatus; +import org.opendaylight.controller.cluster.datastore.messages.GetKnownClients; +import org.opendaylight.controller.cluster.datastore.messages.GetKnownClientsReply; import org.opendaylight.controller.cluster.datastore.messages.GetShardRole; import org.opendaylight.controller.cluster.datastore.messages.GetShardRoleReply; import org.opendaylight.controller.cluster.datastore.messages.MakeLeaderLocal; @@ -75,6 +80,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.FlipMemberVotingStatesForAllShardsInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.FlipMemberVotingStatesForAllShardsOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.FlipMemberVotingStatesForAllShardsOutputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.GetKnownClientsForAllShardsInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.GetKnownClientsForAllShardsOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.GetKnownClientsForAllShardsOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.GetPrefixShardRoleInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.GetPrefixShardRoleOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.GetPrefixShardRoleOutputBuilder; @@ -96,6 +104,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.RemoveShardReplicaInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.RemoveShardReplicaOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.RemoveShardReplicaOutputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.get.known.clients._for.all.shards.output.ShardResult1; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.get.known.clients._for.all.shards.output.ShardResult1Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.get.known.clients._for.all.shards.output.shard.result.KnownClientsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.locate.shard.output.member.node.LeaderActorRefBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.locate.shard.output.member.node.LocalBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.member.voting.states.input.MemberVotingState; @@ -295,7 +306,7 @@ public class ClusterAdminRpcService implements ClusterAdminService { }, actorUtils.getClientDispatcher()); final SettableFuture> future = SettableFuture.create(); - makeLeaderLocalAsk.future().onComplete(new OnComplete() { + makeLeaderLocalAsk.future().onComplete(new OnComplete<>() { @Override public void onComplete(final Throwable failure, final Object success) { if (failure != null) { @@ -626,6 +637,52 @@ public class ClusterAdminRpcService implements ClusterAdminService { return returnFuture; } + + @Override + public ListenableFuture> getKnownClientsForAllShards( + final GetKnownClientsForAllShardsInput input) { + final ImmutableMap> allShardReplies = + getAllShardLeadersClients(); + return Futures.whenAllComplete(allShardReplies.values()).call(() -> processReplies(allShardReplies), + MoreExecutors.directExecutor()); + } + + private static RpcResult processReplies( + final ImmutableMap> allShardReplies) { + final List result = new ArrayList<>(allShardReplies.size()); + for (Entry> entry : allShardReplies.entrySet()) { + final ListenableFuture future = entry.getValue(); + final ShardResultBuilder builder = new ShardResultBuilder() + .setDataStoreType(entry.getKey().getDataStoreType()) + .setShardName(entry.getKey().getShardName()); + + final GetKnownClientsReply reply; + try { + reply = Futures.getDone(future); + } catch (ExecutionException e) { + LOG.debug("Shard {} failed to answer", entry.getKey(), e); + result.add(builder.setSucceeded(Boolean.FALSE).setErrorMessage(e.getCause().getMessage()).build()); + continue; + } + + result.add(builder + .setSucceeded(Boolean.TRUE) + .addAugmentation(ShardResult1.class, new ShardResult1Builder() + .setKnownClients(reply.getClients().stream() + .map(client -> new KnownClientsBuilder() + .setMember(client.getFrontendId().getMemberName().toYang()) + .setType(client.getFrontendId().getClientType().toYang()) + .setGeneration(client.getYangGeneration()) + .build()) + .collect(Collectors.toList())) + .build()) + .build()); + } + + return RpcResultBuilder.success(new GetKnownClientsForAllShardsOutputBuilder().setShardResult(result).build()) + .build(); + } + private static ChangeShardMembersVotingStatus toChangeShardMembersVotingStatus(final String shardName, final List memberVotingStatus) { Map serverVotingStatusMap = new HashMap<>(); @@ -756,6 +813,38 @@ public class ClusterAdminRpcService implements ClusterAdminService { return returnFuture; } + private ImmutableMap> getAllShardLeadersClients() { + final ImmutableMap.Builder> builder = + ImmutableMap.builder(); + + addAllShardsClients(builder, DataStoreType.Config, configDataStore.getActorUtils()); + addAllShardsClients(builder, DataStoreType.Operational, operDataStore.getActorUtils()); + + return builder.build(); + } + + private static void addAllShardsClients( + final ImmutableMap.Builder> builder, + final DataStoreType type, final ActorUtils utils) { + for (String shardName : utils.getConfiguration().getAllShardNames()) { + final SettableFuture future = SettableFuture.create(); + builder.put(new ShardIdentifier(type, shardName), future); + + utils.findPrimaryShardAsync(shardName).flatMap( + info -> Patterns.ask(info.getPrimaryShardActor(), GetKnownClients.INSTANCE, SHARD_MGR_TIMEOUT), + utils.getClientDispatcher()).onComplete(new OnComplete<>() { + @Override + public void onComplete(final Throwable failure, final Object success) { + if (failure == null) { + future.set((GetKnownClientsReply) success); + } else { + future.setException(failure); + } + } + }, utils.getClientDispatcher()); + } + } + private static ListenableFuture> newFailedRpcResultFuture(final String message) { return ClusterAdminRpcService.newFailedRpcResultBuilder(message).buildFuture(); } diff --git a/opendaylight/md-sal/sal-cluster-admin-impl/src/main/java/org/opendaylight/controller/cluster/datastore/admin/ShardIdentifier.java b/opendaylight/md-sal/sal-cluster-admin-impl/src/main/java/org/opendaylight/controller/cluster/datastore/admin/ShardIdentifier.java new file mode 100644 index 0000000000..af3a111665 --- /dev/null +++ b/opendaylight/md-sal/sal-cluster-admin-impl/src/main/java/org/opendaylight/controller/cluster/datastore/admin/ShardIdentifier.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.admin; + +import static java.util.Objects.requireNonNull; + +import com.google.common.base.MoreObjects; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.DataStoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.cluster.admin.rev151013.DatastoreShardId; +import org.opendaylight.yangtools.concepts.Identifier; + +final class ShardIdentifier implements Identifier { + private static final long serialVersionUID = 1L; + + private final @NonNull String shardName; + private final @NonNull DataStoreType type; + + ShardIdentifier(final DataStoreType type, final String shardName) { + this.type = requireNonNull(type); + this.shardName = requireNonNull(shardName); + } + + ShardIdentifier(final DatastoreShardId id) { + this(id.getDataStoreType(), id.getShardName()); + } + + public @NonNull String getShardName() { + return shardName; + } + + public @NonNull DataStoreType getDataStoreType() { + return type; + } + + @Override + public int hashCode() { + return type.hashCode() * 31 + shardName.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ShardIdentifier)) { + return false; + } + final ShardIdentifier other = (ShardIdentifier) obj; + return type.equals(other.type) && shardName.equals(other.shardName); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("type", type).add("shardName", shardName).toString(); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/FrontendMetadata.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/FrontendMetadata.java index 0bf23e776c..d5ecbf6ea4 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/FrontendMetadata.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/FrontendMetadata.java @@ -11,6 +11,7 @@ import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import java.util.HashMap; import java.util.Map; @@ -149,4 +150,10 @@ final class FrontendMetadata extends ShardDataTreeMetadata getClients() { + return clients.values().stream() + .map(FrontendClientMetadataBuilder::getIdentifier) + .collect(ImmutableSet.toImmutableSet()); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java index 8c671cc7da..5fa9192ac0 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java @@ -25,6 +25,7 @@ import com.google.common.base.Ticker; import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Range; import java.io.IOException; import java.util.Arrays; @@ -76,6 +77,8 @@ import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction; import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction; import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.ForwardedReadyTransaction; +import org.opendaylight.controller.cluster.datastore.messages.GetKnownClients; +import org.opendaylight.controller.cluster.datastore.messages.GetKnownClientsReply; import org.opendaylight.controller.cluster.datastore.messages.GetShardDataTree; import org.opendaylight.controller.cluster.datastore.messages.MakeLeaderLocal; import org.opendaylight.controller.cluster.datastore.messages.OnDemandShardState; @@ -371,6 +374,8 @@ public class Shard extends RaftActor { onMakeLeaderLocal(); } else if (RESUME_NEXT_PENDING_TRANSACTION.equals(message)) { store.resumeNextPendingTransaction(); + } else if (GetKnownClients.INSTANCE.equals(message)) { + handleGetKnownClients(); } else if (!responseMessageSlicer.handleMessage(message)) { super.handleNonRaftCommand(message); } @@ -597,6 +602,18 @@ public class Shard extends RaftActor { } } + private void handleGetKnownClients() { + final ImmutableSet clients; + if (isLeader()) { + clients = knownFrontends.values().stream() + .map(LeaderFrontendState::getIdentifier) + .collect(ImmutableSet.toImmutableSet()); + } else { + clients = frontendMetadata.getClients(); + } + sender().tell(new GetKnownClientsReply(clients), self()); + } + private boolean hasLeader() { return getLeaderId() != null; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetKnownClients.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetKnownClients.java new file mode 100644 index 0000000000..f1e7fb70b5 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetKnownClients.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.messages; + +import java.io.Serializable; +import org.eclipse.jdt.annotation.NonNull; + +/** + * Request a shard to report the clients it knows about. Shard is required to respond with {@link GetKnownClientsReply}. + */ +public final class GetKnownClients implements Serializable { + private static final long serialVersionUID = 1L; + + public static final @NonNull GetKnownClients INSTANCE = new GetKnownClients(); + + private GetKnownClients() { + + } + + private Object readResolve() { + return INSTANCE; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetKnownClientsReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetKnownClientsReply.java new file mode 100644 index 0000000000..2864141e87 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetKnownClientsReply.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.messages; + +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableSet; +import java.io.Serializable; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; + +/** + * Reply to {@link GetKnownClients}. + */ +public final class GetKnownClientsReply implements Serializable { + private static final long serialVersionUID = 1L; + + private final @NonNull ImmutableSet clients; + + public GetKnownClientsReply(final ImmutableSet clients) { + this.clients = requireNonNull(clients); + } + + public @NonNull ImmutableSet getClients() { + return clients; + } +} -- 2.36.6