Fix eos entity lookups with YangInstanceIdentifier 81/98281/4
authorTomas Cere <tomas.cere@pantheon.tech>
Wed, 3 Nov 2021 11:20:45 +0000 (12:20 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 4 Nov 2021 18:21:43 +0000 (19:21 +0100)
Caused by entity-name only being a string type which was inadvertently
trying to lookup all entities as General.

Fix this by changing entity-name to a union of instance-identifier and
string.

Also changed up the output of get-entities slightly:
- if the entity is string based it will only contain the name in the
  output.
- if, however, it is Instance-identifier based it will contain the
  entire identifier in the name field.

JIRA: CONTROLLER-2009
Change-Id: I93e108bd54b14850b6bf276ec1eca4d7671d7d87
Signed-off-by: Tomas Cere <tomas.cere@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
12 files changed:
opendaylight/md-sal/eos-dom-akka/pom.xml
opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/controller/eos/akka/AkkaEntityOwnershipService.java
opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/controller/eos/akka/bootstrap/EOSMain.java
opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/controller/eos/akka/owner/supervisor/IdleSupervisor.java
opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/controller/eos/akka/owner/supervisor/OwnerSupervisor.java
opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/controller/eos/akka/owner/supervisor/OwnerSyncer.java
opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/controller/eos/akka/owner/supervisor/command/GetEntitiesReply.java
opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/controller/entity/owners/norev/EntityNameBuilder.java [new file with mode: 0644]
opendaylight/md-sal/eos-dom-akka/src/main/yang/odl-akka-eos.yang
opendaylight/md-sal/eos-dom-akka/src/test/java/org/opendaylight/controller/eos/akka/AbstractNativeEosTest.java
opendaylight/md-sal/eos-dom-akka/src/test/java/org/opendaylight/controller/eos/akka/AkkaEntityOwnershipServiceTest.java
opendaylight/md-sal/eos-dom-akka/src/test/java/org/opendaylight/controller/eos/akka/owner/supervisor/OwnerSupervisorTest.java

index b9b50f58228bea855a9673e7f618333f505efd20..22268e215b0a05f3ea068a016fff4725edefd2f8 100644 (file)
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-dom-codec-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>general-entity</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>concepts</artifactId>
             <artifactId>awaitility</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-dom-codec</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-generator</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-runtime-spi</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-singleton-common-api</artifactId>
             <artifactId>mdsal-singleton-dom-impl</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-topology</artifactId>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
 </project>
index ce60f6f83d26e85aa5f173f60b24ad97e43014ed..e815c8a6a0cc886661c2b38e80106b0fd4743150 100644 (file)
@@ -38,7 +38,6 @@ import org.opendaylight.controller.eos.akka.owner.checker.command.GetOwnershipSt
 import org.opendaylight.controller.eos.akka.owner.checker.command.StateCheckerCommand;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.ActivateDataCenter;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.DeactivateDataCenter;
-import org.opendaylight.controller.eos.akka.owner.supervisor.command.GetEntitiesReply;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.GetEntitiesRequest;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.GetEntityOwnerReply;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.GetEntityOwnerRequest;
@@ -53,6 +52,8 @@ import org.opendaylight.controller.eos.akka.registry.listener.type.command.Regis
 import org.opendaylight.controller.eos.akka.registry.listener.type.command.TypeListenerRegistryCommand;
 import org.opendaylight.controller.eos.akka.registry.listener.type.command.UnregisterListener;
 import org.opendaylight.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
@@ -105,10 +106,12 @@ public class AkkaEntityOwnershipService implements DOMEntityOwnershipService, Da
     private final ActorRef<StateCheckerCommand> ownerStateChecker;
     protected final ActorRef<OwnerSupervisorCommand> ownerSupervisor;
 
+    private final BindingInstanceIdentifierCodec iidCodec;
+
     private Registration reg;
 
     @VisibleForTesting
-    protected AkkaEntityOwnershipService(final ActorSystem actorSystem)
+    protected AkkaEntityOwnershipService(final ActorSystem actorSystem, final BindingCodecTree codecTree)
             throws ExecutionException, InterruptedException {
         final var typedActorSystem = Adapter.toTyped(actorSystem);
         scheduler = typedActorSystem.scheduler();
@@ -121,7 +124,9 @@ public class AkkaEntityOwnershipService implements DOMEntityOwnershipService, Da
             .findFirst()
             .orElseThrow(() -> new IllegalArgumentException("No valid role found."));
 
-        bootstrap = Adapter.spawn(actorSystem, Behaviors.setup(context -> EOSMain.create()), "EOSBootstrap");
+        iidCodec = codecTree.getInstanceIdentifierCodec();
+        bootstrap = Adapter.spawn(actorSystem, Behaviors.setup(
+                context -> EOSMain.create(iidCodec)), "EOSBootstrap");
 
         final CompletionStage<RunningContext> ask = AskPattern.ask(bootstrap,
                 GetRunningContext::new, Duration.ofSeconds(5), scheduler);
@@ -136,8 +141,9 @@ public class AkkaEntityOwnershipService implements DOMEntityOwnershipService, Da
     @Inject
     @Activate
     public AkkaEntityOwnershipService(@Reference final ActorSystemProvider actorProvider,
-            @Reference final RpcProviderService rpcProvider) throws ExecutionException, InterruptedException {
-        this(actorProvider.getActorSystem());
+            @Reference final RpcProviderService rpcProvider, @Reference final BindingCodecTree codecTree)
+            throws ExecutionException, InterruptedException {
+        this(actorProvider.getActorSystem(), codecTree);
 
         reg = rpcProvider.registerRpcImplementation(OdlEntityOwnersService.class, this);
     }
@@ -219,7 +225,7 @@ public class AkkaEntityOwnershipService implements DOMEntityOwnershipService, Da
     @Override
     public ListenableFuture<RpcResult<GetEntitiesOutput>> getEntities(final GetEntitiesInput input) {
         return toRpcFuture(AskPattern.ask(ownerSupervisor, GetEntitiesRequest::new, QUERY_TIMEOUT, scheduler),
-            GetEntitiesReply::toOutput);
+                reply -> reply.toOutput(iidCodec));
     }
 
     @Override
index 4fba4befb9a355fede1bea3bf2730022d9a36665..c0ffa29350f34493ebe3ba76f6ee44903d31b234 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.controller.eos.akka.registry.candidate.CandidateRegistry
 import org.opendaylight.controller.eos.akka.registry.candidate.command.CandidateRegistryCommand;
 import org.opendaylight.controller.eos.akka.registry.listener.type.EntityTypeListenerRegistry;
 import org.opendaylight.controller.eos.akka.registry.listener.type.command.TypeListenerRegistryCommand;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
 import org.opendaylight.yangtools.yang.common.Empty;
 
 public final class EOSMain extends AbstractBehavior<BootstrapCommand> {
@@ -36,7 +37,7 @@ public final class EOSMain extends AbstractBehavior<BootstrapCommand> {
     private final ActorRef<OwnerSupervisorCommand> ownerSupervisor;
     private final ActorRef<StateCheckerCommand> ownerStateChecker;
 
-    private EOSMain(final ActorContext<BootstrapCommand> context) {
+    private EOSMain(final ActorContext<BootstrapCommand> context, final BindingInstanceIdentifierCodec iidCodec) {
         super(context);
 
         final String role = Cluster.get(context.getSystem()).selfMember().getRoles().iterator().next();
@@ -47,11 +48,12 @@ public final class EOSMain extends AbstractBehavior<BootstrapCommand> {
 
         final ClusterSingleton clusterSingleton = ClusterSingleton.get(context.getSystem());
         // start the initial sync behavior that switches to the regular one after syncing
-        ownerSupervisor = clusterSingleton.init(SingletonActor.of(IdleSupervisor.create(), "OwnerSupervisor"));
+        ownerSupervisor = clusterSingleton.init(
+                SingletonActor.of(IdleSupervisor.create(iidCodec), "OwnerSupervisor"));
     }
 
-    public static Behavior<BootstrapCommand> create() {
-        return Behaviors.setup(EOSMain::new);
+    public static Behavior<BootstrapCommand> create(final BindingInstanceIdentifierCodec iidCodec) {
+        return Behaviors.setup(context -> new EOSMain(context, iidCodec));
     }
 
     @Override
index 1abf6d6f12abb1e4cc33aa28f25e15731371d855..7dcfb512189bf5f2b3bb0be3fc3038f48cd65efb 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.eos.akka.owner.supervisor;
 
+import static java.util.Objects.requireNonNull;
+
 import akka.actor.typed.Behavior;
 import akka.actor.typed.javadsl.AbstractBehavior;
 import akka.actor.typed.javadsl.ActorContext;
@@ -16,6 +18,7 @@ import akka.cluster.Member;
 import akka.cluster.typed.Cluster;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.ActivateDataCenter;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.OwnerSupervisorCommand;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -30,8 +33,12 @@ public final class IdleSupervisor extends AbstractBehavior<OwnerSupervisorComman
     private static final String DATACENTER_PREFIX = "dc-";
     private static final String DEFAULT_DATACENTER = "dc-default";
 
-    private IdleSupervisor(final ActorContext<OwnerSupervisorCommand> context) {
+    private final BindingInstanceIdentifierCodec iidCodec;
+
+    private IdleSupervisor(final ActorContext<OwnerSupervisorCommand> context,
+                           final BindingInstanceIdentifierCodec iidCodec) {
         super(context);
+        this.iidCodec = requireNonNull(iidCodec);
         final Cluster cluster = Cluster.get(context.getSystem());
 
         final String datacenterRole = extractDatacenterRole(cluster.selfMember());
@@ -43,9 +50,9 @@ public final class IdleSupervisor extends AbstractBehavior<OwnerSupervisorComman
         LOG.debug("Idle supervisor started on {}.", cluster.selfMember());
     }
 
-    public static Behavior<OwnerSupervisorCommand> create() {
+    public static Behavior<OwnerSupervisorCommand> create(final BindingInstanceIdentifierCodec iidCodec) {
 
-        return Behaviors.setup(IdleSupervisor::new);
+        return Behaviors.setup(context -> new IdleSupervisor(context, iidCodec));
     }
 
     @Override
@@ -57,7 +64,7 @@ public final class IdleSupervisor extends AbstractBehavior<OwnerSupervisorComman
 
     private Behavior<OwnerSupervisorCommand> onActivateDataCenter(final ActivateDataCenter message) {
         LOG.debug("Received ActivateDataCenter command switching to syncer behavior,");
-        return OwnerSyncer.create(message.getReplyTo());
+        return OwnerSyncer.create(message.getReplyTo(), iidCodec);
     }
 
     private String extractDatacenterRole(final Member selfMember) {
index e56ed59f3cfcd7f8cc521c0b3bb50eb557e44330..f66f25aa821d2267cc18633b9752e626171da23f 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.controller.eos.akka.owner.supervisor;
 
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
 import akka.actor.typed.ActorRef;
 import akka.actor.typed.Behavior;
 import akka.actor.typed.javadsl.AbstractBehavior;
@@ -58,6 +61,7 @@ import org.opendaylight.controller.eos.akka.owner.supervisor.command.MemberUpEve
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.OwnerChanged;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.OwnerSupervisorCommand;
 import org.opendaylight.controller.eos.akka.registry.candidate.CandidateRegistry;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -97,10 +101,14 @@ public final class OwnerSupervisor extends AbstractBehavior<OwnerSupervisorComma
     private final BiPredicate<DOMEntity, String> reassignPredicate = (entity, candidate) ->
             !isActiveCandidate(candidate) || !isCandidateFor(entity, candidate);
 
+    private final BindingInstanceIdentifierCodec iidCodec;
+
     private OwnerSupervisor(final ActorContext<OwnerSupervisorCommand> context,
                             final Map<DOMEntity, Set<String>> currentCandidates,
-                            final Map<DOMEntity, String> currentOwners) {
+                            final Map<DOMEntity, String> currentOwners,
+                            final BindingInstanceIdentifierCodec iidCodec) {
         super(context);
+        this.iidCodec = requireNonNull(iidCodec);
 
         final DistributedData distributedData = DistributedData.get(context.getSystem());
         final ActorRef<Replicator.Command> replicator = distributedData.replicator();
@@ -150,8 +158,8 @@ public final class OwnerSupervisor extends AbstractBehavior<OwnerSupervisorComma
     }
 
     public static Behavior<OwnerSupervisorCommand> create(final Map<DOMEntity, Set<String>> currentCandidates,
-                                                          final Map<DOMEntity, String> currentOwners) {
-        return Behaviors.setup(ctx -> new OwnerSupervisor(ctx, currentCandidates, currentOwners));
+            final Map<DOMEntity, String> currentOwners, final BindingInstanceIdentifierCodec iidCodec) {
+        return Behaviors.setup(ctx -> new OwnerSupervisor(ctx, currentCandidates, currentOwners, iidCodec));
     }
 
     @Override
@@ -173,7 +181,7 @@ public final class OwnerSupervisor extends AbstractBehavior<OwnerSupervisorComma
     private Behavior<OwnerSupervisorCommand> onDeactivateDatacenter(final DeactivateDataCenter command) {
         LOG.debug("Deactivating Owner Supervisor on {}", cluster.selfMember());
         command.getReplyTo().tell(DataCenterDeactivated.INSTANCE);
-        return IdleSupervisor.create();
+        return IdleSupervisor.create(iidCodec);
     }
 
     private Behavior<OwnerSupervisorCommand> onOwnerChanged(final OwnerChanged command) {
@@ -431,8 +439,14 @@ public final class OwnerSupervisor extends AbstractBehavior<OwnerSupervisorComma
             .collect(Collectors.toSet());
     }
 
-    private static DOMEntity extractEntity(final AbstractEntityRequest<?> request) {
-        return new DOMEntity(request.getType().getValue(), request.getName().getValue());
+    private DOMEntity extractEntity(final AbstractEntityRequest<?> request) {
+        final var name = request.getName();
+        final var iid = name.getInstanceIdentifier();
+        if (iid != null) {
+            return new DOMEntity(request.getType().getValue(), iidCodec.fromBinding(iid));
+        }
+        final var str = verifyNotNull(name.getString(), "Unhandled entity name %s", name);
+        return new DOMEntity(request.getType().getValue(), str);
     }
 
     private static String extractRole(final Member member) {
index a73a5620b39687beb411d8ee911f075b182276d7..04d27ee4dc47b80c1612f4386118535e23d33044 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.eos.akka.owner.supervisor;
 
+import static java.util.Objects.requireNonNull;
+
 import akka.actor.typed.ActorRef;
 import akka.actor.typed.Behavior;
 import akka.actor.typed.javadsl.AbstractBehavior;
@@ -32,6 +34,7 @@ import org.opendaylight.controller.eos.akka.owner.supervisor.command.InitialOwne
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.OwnerSupervisorCommand;
 import org.opendaylight.controller.eos.akka.owner.supervisor.command.OwnerSupervisorReply;
 import org.opendaylight.controller.eos.akka.registry.candidate.CandidateRegistry;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,17 +52,20 @@ public final class OwnerSyncer extends AbstractBehavior<OwnerSupervisorCommand>
 
     // String representation of Entity to DOMEntity
     private final Map<String, DOMEntity> entityLookup = new HashMap<>();
+    private final BindingInstanceIdentifierCodec iidCodec;
 
     private int toSync = -1;
 
     private OwnerSyncer(final ActorContext<OwnerSupervisorCommand> context,
-                        @Nullable final ActorRef<OwnerSupervisorReply> notifyDatacenterStarted) {
+                        final @Nullable ActorRef<OwnerSupervisorReply> notifyDatacenterStarted,
+                        final BindingInstanceIdentifierCodec iidCodec) {
         super(context);
+        this.iidCodec = requireNonNull(iidCodec);
         LOG.debug("Starting candidate and owner sync");
 
         final ActorRef<Replicator.Command> replicator = DistributedData.get(context.getSystem()).replicator();
 
-        this.ownerReplicator = new ReplicatorMessageAdapter<>(context, replicator, Duration.ofSeconds(5));
+        ownerReplicator = new ReplicatorMessageAdapter<>(context, replicator, Duration.ofSeconds(5));
 
         new ReplicatorMessageAdapter<OwnerSupervisorCommand, ORMap<DOMEntity, ORSet<String>>>(context, replicator,
             Duration.ofSeconds(5)).askGet(
@@ -71,9 +77,9 @@ public final class OwnerSyncer extends AbstractBehavior<OwnerSupervisorCommand>
         }
     }
 
-    public static Behavior<OwnerSupervisorCommand> create(
-            final ActorRef<OwnerSupervisorReply> notifyDatacenterStarted) {
-        return Behaviors.setup(ctx -> new OwnerSyncer(ctx, notifyDatacenterStarted));
+    public static Behavior<OwnerSupervisorCommand> create(final ActorRef<OwnerSupervisorReply> notifyDatacenterStarted,
+            final BindingInstanceIdentifierCodec iidCodec) {
+        return Behaviors.setup(ctx -> new OwnerSyncer(ctx, notifyDatacenterStarted, iidCodec));
     }
 
     @Override
@@ -142,8 +148,7 @@ public final class OwnerSyncer extends AbstractBehavior<OwnerSupervisorCommand>
     private Behavior<OwnerSupervisorCommand> switchToSupervisor() {
         LOG.debug("Initial sync done, switching to supervisor. candidates: {}, owners: {}",
                 currentCandidates, currentOwners);
-        return Behaviors.setup(ctx ->
-                OwnerSupervisor.create(currentCandidates, currentOwners));
+        return Behaviors.setup(ctx -> OwnerSupervisor.create(currentCandidates, currentOwners, iidCodec));
     }
 
     private void handleOwnerRsp(final Replicator.GetSuccess<LWWRegister<String>> rsp) {
index ea8a5a8cc1007f1ec5266f494ede5b23a1a6aa89..1cfa31ab0052a4eb2be52f7f08c75f0175a637aa 100644 (file)
@@ -20,6 +20,7 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.EntityName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.EntityType;
@@ -27,6 +28,7 @@ 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.entity.owners.norev.GetEntitiesOutputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.NodeName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.get.entities.output.EntitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.core.general.entity.rev150930.Entity;
 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
@@ -46,7 +48,7 @@ public final class GetEntitiesReply extends OwnerSupervisorReply implements Seri
         this.owners = ImmutableMap.copyOf(owners);
     }
 
-    public @NonNull GetEntitiesOutput toOutput() {
+    public @NonNull GetEntitiesOutput toOutput(final BindingInstanceIdentifierCodec iidCodec) {
         final Set<DOMEntity> entities = new HashSet<>();
         entities.addAll(owners.keySet());
         entities.addAll(candidates.keySet());
@@ -56,7 +58,7 @@ public final class GetEntitiesReply extends OwnerSupervisorReply implements Seri
                 .map(entity -> {
                     final EntitiesBuilder eb = new EntitiesBuilder()
                         .setType(new EntityType(entity.getType()))
-                        .setName(extractName(entity))
+                        .setName(extractName(entity, iidCodec))
                         .setCandidateNodes(candidates.get(entity).stream()
                             .map(NodeName::new).collect(Collectors.toUnmodifiableList()));
 
@@ -70,8 +72,21 @@ public final class GetEntitiesReply extends OwnerSupervisorReply implements Seri
             .build();
     }
 
-    private static EntityName extractName(final DOMEntity entity) {
-        final PathArgument last = entity.getIdentifier().getLastPathArgument();
+    /**
+     * if the entity is general entity then shorthand the name to only the last path argument, otherwise return
+     * full YIID path encoded as string.
+     *
+     * @param entity Entity to extract the name from
+     * @param iidCodec codec to encode entity name back to InstanceIdentifier if needed
+     * @return Extracted name
+     */
+    private static EntityName extractName(final DOMEntity entity, final BindingInstanceIdentifierCodec iidCodec) {
+        final var id = entity.getIdentifier();
+        if (id.isEmpty() || !id.getPathArguments().get(0).getNodeType().equals(Entity.QNAME)) {
+            return new EntityName(iidCodec.toBinding(id));
+        }
+
+        final PathArgument last = id.getLastPathArgument();
         verify(last instanceof NodeIdentifierWithPredicates, "Unexpected last argument %s", last);
         final Object value = Iterables.getOnlyElement(((NodeIdentifierWithPredicates) last).values());
         verify(value instanceof String, "Unexpected predicate value %s", value);
diff --git a/opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/controller/entity/owners/norev/EntityNameBuilder.java b/opendaylight/md-sal/eos-dom-akka/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/controller/entity/owners/norev/EntityNameBuilder.java
new file mode 100644 (file)
index 0000000..f67d2cc
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021 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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev;
+
+public final class EntityNameBuilder {
+    private EntityNameBuilder() {
+        // Hidden on purpose
+    }
+
+    public static EntityName getDefaultInstance(final String defaultValue) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+}
index b4f16b9469aa2ce35a54b89d2dad88c8e6e404b1..1aa2d41bbcc247836e9c3017381a11faa28066a5 100644 (file)
@@ -19,8 +19,11 @@ module odl-entity-owners {
   }
 
   typedef entity-name {
-    type string {
-      length 1..max;
+    type union {
+      type instance-identifier;
+      type string {
+        length 1..max;
+      }
     }
   }
 
index 5af0e2afdbffffa48aea202e4b2827ae998db2f0..365ef85676db447caed3ed916d9b11ee653b9dfd 100644 (file)
@@ -49,6 +49,10 @@ import org.opendaylight.controller.eos.akka.registry.candidate.command.RegisterC
 import org.opendaylight.controller.eos.akka.registry.candidate.command.UnregisterCandidate;
 import org.opendaylight.controller.eos.akka.registry.listener.type.command.RegisterListener;
 import org.opendaylight.controller.eos.akka.registry.listener.type.command.TypeListenerRegistryCommand;
+import org.opendaylight.mdsal.binding.dom.codec.impl.BindingCodecContext;
+import org.opendaylight.mdsal.binding.generator.impl.DefaultBindingRuntimeGenerator;
+import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeGenerator;
+import org.opendaylight.mdsal.binding.runtime.spi.BindingRuntimeHelpers;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener;
@@ -77,6 +81,11 @@ public abstract class AbstractNativeEosTest {
                     "akka://ClusterSystem@127.0.0.1:2552",
                     "akka://ClusterSystem@127.0.0.1:2553");
 
+    private static final BindingRuntimeGenerator BINDING_RUNTIME_GENERATOR = new DefaultBindingRuntimeGenerator();
+
+    protected static BindingCodecContext CODEC_CONTEXT
+            = new BindingCodecContext(BindingRuntimeHelpers.createRuntimeContext());
+
     private static final String REMOTE_PROTOCOL = "akka";
     private static final String PORT_PARAM = "akka.remote.artery.canonical.port";
     private static final String ROLE_PARAM = "akka.cluster.roles";
@@ -168,7 +177,7 @@ public abstract class AbstractNativeEosTest {
         // Create a classic Akka system since thats what we will have in osgi
         final akka.actor.ActorSystem system = akka.actor.ActorSystem.create("ClusterSystem", config);
         final ActorRef<BootstrapCommand> eosBootstrap =
-                Adapter.spawn(system, EOSMain.create(), "EOSBootstrap");
+                Adapter.spawn(system, EOSMain.create(CODEC_CONTEXT.getInstanceIdentifierCodec()), "EOSBootstrap");
 
         final CompletionStage<RunningContext> ask = AskPattern.ask(eosBootstrap,
                 GetRunningContext::new,
@@ -181,7 +190,7 @@ public abstract class AbstractNativeEosTest {
     }
 
     private static Behavior<BootstrapCommand> rootBehavior() {
-        return Behaviors.setup(context -> EOSMain.create());
+        return Behaviors.setup(context -> EOSMain.create(CODEC_CONTEXT.getInstanceIdentifierCodec()));
     }
 
     protected static void registerCandidates(final ClusterNode node, final DOMEntity entity, final String... members) {
@@ -388,7 +397,7 @@ public abstract class AbstractNativeEosTest {
 
         protected MockNativeEntityOwnershipService(ActorSystem classicActorSystem)
                 throws ExecutionException, InterruptedException {
-            super(classicActorSystem);
+            super(classicActorSystem, CODEC_CONTEXT);
             this.classicActorSystem = classicActorSystem;
         }
 
index cf456fec75ea65cbcd31052d6d147cad6d57ec79..b707b84f2ec1cef04187c53c1c52d716af14bdc6 100644 (file)
@@ -11,6 +11,7 @@ import static org.awaitility.Awaitility.await;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -42,14 +43,23 @@ import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration;
 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.EntityName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.EntityType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntityInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntityOutput;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 
 public class AkkaEntityOwnershipServiceTest extends AbstractNativeEosTest {
     static final String ENTITY_TYPE = "test";
     static final String ENTITY_TYPE2 = "test2";
     static final QName QNAME = QName.create("test", "2015-08-11", "foo");
-    static int ID_COUNTER = 1;
 
     private ActorSystem system;
     private akka.actor.typed.ActorSystem<Void> typedSystem;
@@ -62,7 +72,7 @@ public class AkkaEntityOwnershipServiceTest extends AbstractNativeEosTest {
         typedSystem = Adapter.toTyped(this.system);
         replicator = DistributedData.get(typedSystem).replicator();
 
-        service = new AkkaEntityOwnershipService(system);
+        service = new AkkaEntityOwnershipService(system, CODEC_CONTEXT);
     }
 
     @After
@@ -175,6 +185,44 @@ public class AkkaEntityOwnershipServiceTest extends AbstractNativeEosTest {
         assertTrue(service.isCandidateRegistered(test));
     }
 
+    @Test
+    public void testEntityRetrievalWithYiid() throws Exception {
+        final YangInstanceIdentifier entityId = YangInstanceIdentifier.create(new NodeIdentifier(NetworkTopology.QNAME),
+                new NodeIdentifier(Topology.QNAME),
+                NodeIdentifierWithPredicates.of(Topology.QNAME, QName.create(Topology.QNAME, "topology-id"), "test"),
+                new NodeIdentifier(Node.QNAME),
+                NodeIdentifierWithPredicates.of(Node.QNAME, QName.create(Node.QNAME, "node-id"), "test://test-node"));
+
+        final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
+
+        final DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(entity);
+
+        verifyEntityOwnershipCandidateRegistration(entity, reg);
+        verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
+
+        RpcResult<GetEntityOutput> getEntityResult = service.getEntity(new GetEntityInputBuilder()
+                .setName(new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)))
+                .setType(new EntityType(ENTITY_TYPE))
+                .build())
+                .get();
+
+        assertEquals(getEntityResult.getResult().getOwnerNode().getValue(), "member-1");
+        assertEquals(getEntityResult.getResult().getCandidateNodes().get(0).getValue(), "member-1");
+
+        // we should not be able to retrieve the entity when using string
+        final String entityPathEncoded =
+                "/network-topology:network-topology/topology[topology-id='test']/node[node-id='test://test-node']";
+
+        getEntityResult = service.getEntity(new GetEntityInputBuilder()
+                .setName(new EntityName(entityPathEncoded))
+                .setType(new EntityType(ENTITY_TYPE))
+                .build())
+                .get();
+
+        assertNull(getEntityResult.getResult().getOwnerNode());
+        assertTrue(getEntityResult.getResult().getCandidateNodes().isEmpty());
+    }
+
     private static void verifyGetOwnershipState(final DOMEntityOwnershipService service, final DOMEntity entity,
                                                 final EntityOwnershipState expState) {
         await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
index 8a088e3fdbcef8c48ffb115b8d154aa23cf07f4f..b396053e29815683ab15fda72a5fc26095800db5 100644 (file)
@@ -139,7 +139,7 @@ public class OwnerSupervisorTest extends AbstractNativeEosTest {
         }
 
         private Behavior<OwnerSupervisorCommand> switchToSupervisor(final InitialCandidateSync message) {
-            return OwnerSupervisor.create(currentCandidates, currentOwners);
+            return OwnerSupervisor.create(currentCandidates, currentOwners, CODEC_CONTEXT.getInstanceIdentifierCodec());
         }
     }