VPP API <-> YANG translation layer integration
authorMarek Gradzki <[email protected]>
Tue, 29 Mar 2016 11:37:36 +0000 (13:37 +0200)
committerMarek Gradzki <[email protected]>
Thu, 31 Mar 2016 14:14:27 +0000 (16:14 +0200)
Prototype that uses Readers API for non-list nodes.

Change-Id: I482dcfe09d094456b014e55edbc38ce6e98afcc3
Signed-off-by: Marek Gradzki <[email protected]>
Signed-off-by: Maros Marsalek <[email protected]>
12 files changed:
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/ReadableVppDataTree.java
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java
v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java
v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java
v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateTest.java

index 54acce3..8a0dead 100644 (file)
@@ -21,6 +21,7 @@ import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import io.fd.honeycomb.v3po.impl.data.VppDataBrokerInitializationProvider;
+import io.fd.honeycomb.v3po.impl.data.VppReaderRegistry;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
@@ -187,8 +188,11 @@ public class V3poProvider implements BindingAwareProvider, AutoCloseable {
                                                    vppPollOperData);
         startOperationalUpdateTimer();
 
-        // TODO make configurable
-        vppDataBrokerInitializationProvider = new VppDataBrokerInitializationProvider(db);
+        final VppReaderRegistry readerRegistry = VppReaderRegistry.getInstance(api);
+
+        // TODO make configurable:
+        vppDataBrokerInitializationProvider = new VppDataBrokerInitializationProvider(db, readerRegistry);
+        // TODO pull the registration into Module
         domBroker.registerProvider(vppDataBrokerInitializationProvider);
     }
 
index 18e854a..19248dd 100644 (file)
@@ -19,6 +19,7 @@ package io.fd.honeycomb.v3po.impl.data;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -34,5 +35,5 @@ public interface ReadableVppDataTree {
      * @param path Path of the node
      * @return a CheckFuture containing the result of the read.
      */
-    CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(YangInstanceIdentifier path);
+    CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path);
 }
index 66bcf48..41a68e7 100644 (file)
@@ -136,7 +136,11 @@ public final class VppConfigDataTree implements VppDataTree {
         @Override
         public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
                 final YangInstanceIdentifier path) {
-            return Futures.immediateCheckedFuture(snapshot.readNode(path));
+            final Optional<NormalizedNode<?, ?>> node = snapshot.readNode(path);
+            if (LOG.isTraceEnabled() && node.isPresent()) {
+                LOG.trace("ConfigSnapshot.read: {}", node.get());
+            }
+            return Futures.immediateCheckedFuture(node);
         }
 
         @Override
@@ -183,7 +187,7 @@ public final class VppConfigDataTree implements VppDataTree {
         }
 
         void revertChanges() throws VppApiInvocationException {
-            Preconditions.checkNotNull(writer, "VppWriter is nuserializerll!");
+            Preconditions.checkNotNull(writer, "VppWriter is null!");
 
             // revert changes in reverse order they were applied
             final ListIterator<InstanceIdentifier<?>> iterator = processedNodes.listIterator(processedNodes.size());
index 50181e2..b7a24ea 100644 (file)
@@ -22,8 +22,8 @@ import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import io.fd.honeycomb.v3po.impl.LoggingFuturesCallBack;
+import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
 import io.fd.honeycomb.v3po.impl.trans0.DefaultVppWriter;
-import io.fd.honeycomb.v3po.impl.trans0.VppInterfacesReader;
 import java.util.Collection;
 import java.util.Collections;
 import javassist.ClassPool;
@@ -43,7 +43,8 @@ import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.controller.sal.core.api.Provider;
 import org.opendaylight.controller.sal.core.api.model.SchemaService;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.BridgeDomains;
 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.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
@@ -81,11 +82,13 @@ public final class VppDataBrokerInitializationProvider implements Provider, Auto
     private final TopologyId VPP_TOPOLOGY_ID = TopologyId.getDefaultInstance("vpp-topology");
     private final NodeId VPP_TOPOLOGY_NODE_ID = NodeId.getDefaultInstance("vpp");
     private final DataBroker bindingBroker;
+    private final ReaderRegistry readerRegistry;
     private final InstanceIdentifier<Node> mountPointPath;
     private ObjectRegistration<DOMMountPoint> mountPointRegistration;
 
-    public VppDataBrokerInitializationProvider(@Nonnull final DataBroker bindingBroker) {
+    public VppDataBrokerInitializationProvider(@Nonnull final DataBroker bindingBroker, final ReaderRegistry readerRegistry) {
         this.bindingBroker = Preconditions.checkNotNull(bindingBroker, "bindingBroker should not be null");
+        this.readerRegistry = Preconditions.checkNotNull(readerRegistry, "readerRegistry should not be null");
         this.mountPointPath = getMountPointPath();
     }
 
@@ -123,7 +126,7 @@ public final class VppDataBrokerInitializationProvider implements Provider, Auto
 
         createMountPointPlaceholder();
 
-        initialVppStateSynchronization(broker);
+        initialVppConfigSynchronization(broker);
     }
 
     @Override
@@ -170,12 +173,13 @@ public final class VppDataBrokerInitializationProvider implements Provider, Auto
     private DOMDataBroker initVppDataBroker(final SchemaContext globalContext,
                                             final BindingNormalizedNodeSerializer serializer) {
         final ReadableVppDataTree operationalData =
-                new VppOperationalDataTree(serializer, new VppInterfacesReader()); // TODO make configurable
+                new VppOperationalDataTree(serializer, globalContext, readerRegistry); // TODO make configurable
 
         final DataTree dataTree =
                 InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); // TODO make configurable
         dataTree.setSchemaContext(globalContext);
 
+        // FIXME use the new writer API
         final VppDataTree configDataProxy = new VppConfigDataTree(serializer, dataTree,
                 new DefaultVppWriter()); // TODO make configurable
         return new VppDataBroker(operationalData, configDataProxy);
@@ -202,21 +206,32 @@ public final class VppDataBrokerInitializationProvider implements Provider, Auto
 
     // TODO operational and config models are not 1-1
     // decide what part of operational data should be written to config during initialization
-    private void initialVppStateSynchronization(final DOMDataBroker broker) {
+    private void initialVppConfigSynchronization(final DOMDataBroker broker) {
         // read from operational
         final DOMDataReadOnlyTransaction readTx = broker.newReadOnlyTransaction();
 
-        final YangInstanceIdentifier interfacesID = YangInstanceIdentifier.of(Interfaces.QNAME);
+        final YangInstanceIdentifier
+            id = YangInstanceIdentifier.builder().node(VppState.QNAME).node(BridgeDomains.QNAME).build();
+
+        LOG.trace("initialVppStateSynchronization id: {}", id);
 
         final ListenableFuture<Void> writeFuture = Futures.transform(
-                readTx.read(LogicalDatastoreType.OPERATIONAL, interfacesID),
+                readTx.read(LogicalDatastoreType.OPERATIONAL, id),
                 new AsyncFunction<Optional<NormalizedNode<?, ?>>, Void>() {
                     @Override
                     public ListenableFuture<Void> apply(final Optional<NormalizedNode<?, ?>> readResult)
                             throws Exception {
                         if (readResult.isPresent()) {
                             final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction();
-                            writeTx.put(LogicalDatastoreType.CONFIGURATION, interfacesID, readResult.get());
+                            final NormalizedNode<?, ?> node = readResult.get();
+                            LOG.trace("Read result: {}", node);
+
+                            // FIXME
+                            // this will fail because we are reading OPERATIONAL data and writing to CONFIGURATION
+                            // we need to provide extensible way to register initializer that would
+                            // translate between models
+
+                            // writeTx.put(LogicalDatastoreType.CONFIGURATION, id, node);
                             return writeTx.submit();
                         } else {
                             return Futures
index fbfd910..c378365 100644 (file)
 
 package io.fd.honeycomb.v3po.impl.data;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Multimap;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
-import io.fd.honeycomb.v3po.impl.trans0.VppReader;
+import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
+import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 /**
  * ReadableVppDataTree implementation for operational data.
  */
 public final class VppOperationalDataTree implements ReadableVppDataTree {
     private static final Logger LOG = LoggerFactory.getLogger(VppOperationalDataTree.class);
+
     private final BindingNormalizedNodeSerializer serializer;
-    private final VppReader reader;
+    private final ReaderRegistry readerRegistry;
+    private final SchemaContext globalContext;
 
     /**
      * Creates operational data tree instance.
      *
-     * @param serializer service for serialization between Java Binding Data representation and NormalizedNode
-     *                   representation.
-     * @param reader     service for translation between Vpp and Java Binding Data.
+     * @param serializer     service for serialization between Java Binding Data representation and NormalizedNode
+     *                       representation.
+     * @param globalContext  service for obtaining top level context data from all yang modules.
+     * @param readerRegistry service responsible for translation between DataObjects and VPP APIs.
      */
     public VppOperationalDataTree(@Nonnull BindingNormalizedNodeSerializer serializer,
-                                  @Nonnull VppReader reader) {
-        this.serializer = Preconditions.checkNotNull(serializer, "serializer should not be null");
-        this.reader = Preconditions.checkNotNull(reader, "reader should not be null");
+                                  @Nonnull final SchemaContext globalContext, @Nonnull ReaderRegistry readerRegistry) {
+        this.globalContext = checkNotNull(globalContext, "serializer should not be null");
+        this.serializer = checkNotNull(serializer, "serializer should not be null");
+        this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null");
     }
 
     @Override
     public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
-            final YangInstanceIdentifier yangInstanceIdentifier) {
-        // TODO What if the path is ROOT/empty?
+            @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) {
+
+        if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
+            LOG.debug("VppOperationalDataProxy.read(), yangInstanceIdentifier=ROOT");
+            return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>of(readRoot()));
+        }
+
+        LOG.debug("VppOperationalDataProxy.read(), yangInstanceIdentifier={}", yangInstanceIdentifier);
         final InstanceIdentifier<?> path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier);
+        if (path == null) {
+            // TODO try to translate wildcarded identifiers here as a workaround if it is expected to be used that way
+            // Currently its not possible to read list using wildcarded ID. SO we may not need this at all.
+        }
+        checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier);
         LOG.debug("VppOperationalDataProxy.read(), path={}", path);
 
-        final DataObject dataObject = reader.read(path); // FIXME we need to expect a list of dataObjects here
-        return Futures.immediateCheckedFuture(toNormalizedNode(path, dataObject));
+        final List<? extends DataObject> dataObjects = readerRegistry.read(path);
+
+        if (dataObjects.isEmpty()) {
+            return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>absent());
+        }
+
+        final NormalizedNode<?, ?> value = wrapDataObjects(yangInstanceIdentifier, path, dataObjects);
+        return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>fromNullable(value));
     }
 
-    private Optional<NormalizedNode<?, ?>> toNormalizedNode(final InstanceIdentifier path,
-                                                            final DataObject dataObject) {
-        LOG.trace("VppOperationalDataProxy.toNormalizedNode(), path={}, path={}", path, dataObject);
-        final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
-                serializer.toNormalizedNode(path, dataObject);
+    private DataSchemaNode getSchemaNode(final @Nonnull YangInstanceIdentifier yangInstanceIdentifier) {
+        return globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
+    }
+
+    private NormalizedNode<?, ?> readRoot() {
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> dataNodeBuilder =
+                Builders.containerBuilder()
+                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME));
+
+        final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects =
+                readerRegistry.readAll();
+
+        for (final InstanceIdentifier<? extends DataObject> instanceIdentifier : dataObjects.keySet()) {
+            final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier);
+            final NormalizedNode<?, ?> node =
+                    wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier));
+            dataNodeBuilder.withChild((DataContainerChild<?, ?>) node);
+        }
+
+        return dataNodeBuilder.build();
+    }
+
+    private NormalizedNode<?, ?> wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier,
+                                              final InstanceIdentifier<? extends DataObject> instanceIdentifier,
+                                              final Collection<? extends DataObject> dataObjects) {
+        final Collection<NormalizedNode<?, ?>> normalizedRootElements = Collections2
+                .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier));
+
+        final DataSchemaNode schemaNode = getSchemaNode(yangInstanceIdentifier);
+        if (schemaNode instanceof ListSchemaNode) {
+            // In case of a list, wrap all the values in a Mixin parent node
+            final ListSchemaNode listSchema = (ListSchemaNode) schemaNode;
+            return wrapListIntoMixinNode(normalizedRootElements, listSchema);
+        } else {
+            Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected");
+            return getOnlyElement(normalizedRootElements);
+        }
+    }
+
+    private static DataContainerChild<?, ?> wrapListIntoMixinNode(
+        final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
+        if (listSchema.getKeyDefinition().isEmpty()) {
+            final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> listBuilder =
+                Builders.unkeyedListBuilder();
+            for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
+                listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement);
+            }
+            return listBuilder.build();
+        } else {
+            final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> listBuilder =
+                listSchema.isUserOrdered()
+                    ? Builders.orderedMapBuilder()
+                    : Builders.mapBuilder();
+
+            for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
+                listBuilder.withChild((MapEntryNode) normalizedRootElement);
+            }
+            return listBuilder.build();
+        }
+    }
 
-        final NormalizedNode<?, ?> value = entry.getValue();
-        LOG.trace("VppOperationalDataProxy.toNormalizedNode(), value={}", value);
+    @SuppressWarnings("unchecked")
+    private Function<DataObject, NormalizedNode<?, ?>> toNormalizedNodeFunction(final InstanceIdentifier path) {
+        return new Function<DataObject, NormalizedNode<?, ?>>() {
+            @Override
+            public NormalizedNode<?, ?> apply(@Nullable final DataObject dataObject) {
+                LOG.trace("VppOperationalDataProxy.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
+                final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
+                        serializer.toNormalizedNode(path, dataObject);
 
-        final Optional<NormalizedNode<?, ?>> optional = Optional.<NormalizedNode<?, ?>>fromNullable(value);
-        LOG.trace("VppOperationalDataProxy.toNormalizedNode(), optional={}", optional);
-        return optional;
+                LOG.trace("VppOperationalDataProxy.toNormalizedNode(), normalizedNodeEntry={}", entry);
+                return entry.getValue();
+            }
+        };
     }
 }
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java
new file mode 100644 (file)
index 0000000..c5d4a81
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.impl.data;
+
+import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.impl.trans.r.ChildVppReader;
+import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
+import io.fd.honeycomb.v3po.impl.trans.r.VppReader;
+import io.fd.honeycomb.v3po.impl.trans.r.impl.CompositeChildVppReader;
+import io.fd.honeycomb.v3po.impl.trans.r.impl.CompositeListVppReader;
+import io.fd.honeycomb.v3po.impl.trans.r.impl.CompositeRootVppReader;
+import io.fd.honeycomb.v3po.impl.trans.r.util.DelegatingReaderRegistry;
+import io.fd.honeycomb.v3po.impl.trans.r.util.ReflexiveChildReaderCustomizer;
+import io.fd.honeycomb.v3po.impl.trans.r.util.ReflexiveRootReaderCustomizer;
+import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
+import io.fd.honeycomb.v3po.impl.vppstate.BridgeDomainCustomizer;
+import io.fd.honeycomb.v3po.impl.vppstate.VersionCustomizer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.BridgeDomains;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.BridgeDomainsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.Version;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomainBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomainKey;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.vppjapi.vppApi;
+
+// TODO use some DI framework instead of singleton
+public class VppReaderRegistry implements ReaderRegistry {
+
+    private static VppReaderRegistry instance;
+
+    private final DelegatingReaderRegistry reader;
+
+    private VppReaderRegistry(@Nonnull final vppApi vppApi) {
+        final CompositeRootVppReader<VppState, VppStateBuilder> vppStateReader = initVppStateReader(vppApi);
+        // TODO add more root readers
+        reader = new DelegatingReaderRegistry(Collections.<VppReader<? extends DataObject>>singletonList(vppStateReader));
+    }
+
+    private static CompositeRootVppReader<VppState, VppStateBuilder> initVppStateReader(@Nonnull final vppApi vppApi) {
+
+        final ChildVppReader<Version> versionReader = new CompositeChildVppReader<>(
+                Version.class, new VersionCustomizer(vppApi));
+
+        final CompositeListVppReader<BridgeDomain, BridgeDomainKey, BridgeDomainBuilder>
+                bridgeDomainReader = new CompositeListVppReader<>(
+                BridgeDomain.class,
+                new BridgeDomainCustomizer(vppApi));
+
+        final ChildVppReader<BridgeDomains> bridgeDomainsReader = new CompositeChildVppReader<>(
+                BridgeDomains.class,
+                VppRWUtils.singletonChildReaderList(bridgeDomainReader),
+                new ReflexiveChildReaderCustomizer<>(BridgeDomainsBuilder.class));
+
+        final List<ChildVppReader<? extends ChildOf<VppState>>> childVppReaders = new ArrayList<>();
+        childVppReaders.add(versionReader);
+        childVppReaders.add(bridgeDomainsReader);
+
+        return new CompositeRootVppReader<>(
+                VppState.class,
+                childVppReaders,
+                VppRWUtils.<VppState>emptyAugReaderList(),
+                new ReflexiveRootReaderCustomizer<>(VppStateBuilder.class));
+    }
+
+    public static synchronized VppReaderRegistry getInstance(@Nonnull final vppApi vppApi) {
+        if (instance == null) {
+            instance = new VppReaderRegistry(vppApi);
+        }
+        return instance;
+    }
+
+    @Nonnull
+    @Override
+    public Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll() {
+        return reader.readAll();
+    }
+
+    @Nonnull
+    @Override
+    public List<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id) {
+        return reader.read(id);
+    }
+
+    @Nonnull
+    @Override
+    public InstanceIdentifier<DataObject> getManagedDataObjectType() {
+        return reader.getManagedDataObjectType();
+    }
+}
index 49705fe..8c592a6 100644 (file)
 package io.fd.honeycomb.v3po.impl.trans.r;
 
 import com.google.common.annotations.Beta;
-import java.util.List;
+import com.google.common.collect.Multimap;
 import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 /**
- * Simple delegating reader suitable as a holder for all other root readers, providing readAll feature
+ * Simple delegating reader suitable as a holder for all other root readers, providing readAll feature.
  */
 @Beta
 public interface ReaderRegistry extends VppReader<DataObject> {
 
     /**
-     * Perform read on all underlying readers and merge the results into a single list
+     * Performs read on all registered root readers and merges the results into a Multimap.
+     * Keys represent identifiers for root DataObjects from the data tree modeled by YANG.
+     *
+     * @return multimap that preserves deterministic iteration order across non-distinct key values
      */
     @Nonnull
-    List<? extends DataObject> readAll();
+    Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll();
 }
index 033e01b..4e50e5a 100644 (file)
@@ -19,10 +19,11 @@ package io.fd.honeycomb.v3po.impl.trans.r.util;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.collect.Iterables;
-import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
 import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
 import io.fd.honeycomb.v3po.impl.trans.r.VppReader;
-import java.util.ArrayList;
+import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.Nonnull;
@@ -54,15 +55,15 @@ public final class DelegatingReaderRegistry implements ReaderRegistry {
 
     @Override
     @Nonnull
-    public List<? extends DataObject> readAll() {
-        LOG.debug("Reading from all delegates");
+    public Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll() {
+        LOG.debug("Reading from all delegates: {}", this);
         LOG.trace("Reading from all delegates: {}", rootReaders.values());
 
-        final List<DataObject> objects = new ArrayList<>(rootReaders.size());
+        final Multimap<InstanceIdentifier<? extends DataObject>, DataObject> objects = LinkedListMultimap.create();
         for (VppReader<? extends DataObject> rootReader : rootReaders.values()) {
             LOG.debug("Reading from delegate: {}", rootReader);
             final List<? extends DataObject> read = rootReader.read(rootReader.getManagedDataObjectType());
-            objects.addAll(read);
+            objects.putAll(rootReader.getManagedDataObjectType(), read);
         }
         return objects;
     }
index 1930a83..07193a1 100644 (file)
@@ -19,7 +19,6 @@ package io.fd.honeycomb.v3po.impl.vppstate;
 import com.google.common.collect.Lists;
 import io.fd.honeycomb.v3po.impl.trans.r.impl.spi.ListVppReaderCustomizer;
 import io.fd.honeycomb.v3po.impl.trans.util.VppApiCustomizer;
-import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
 import java.util.ArrayList;
 import java.util.List;
 import javax.annotation.Nonnull;
@@ -66,7 +65,9 @@ public final class BridgeDomainCustomizer extends VppApiCustomizer
 
         builder.setInterface(getIfcs(bridgeDomainDetails));
 
-        final vppL2Fib[] vppL2Fibs = getVppApi().l2FibTableDump(bdId);
+        // final vppL2Fib[] vppL2Fibs = getVppApi().l2FibTableDump(bdId); FIXME we need writer for L2Fib
+        final vppL2Fib[] vppL2Fibs = getL2Fibs(bdId);
+
         final List<L2Fib> l2Fibs = Lists.newArrayListWithCapacity(vppL2Fibs.length);
         for (vppL2Fib vppL2Fib : vppL2Fibs) {
             l2Fibs.add(new L2FibBuilder()
@@ -82,6 +83,22 @@ public final class BridgeDomainCustomizer extends VppApiCustomizer
         builder.setL2Fib(l2Fibs);
     }
 
+    // FIXME remove when list read is implemented
+    // updating L2Fib was BD was is implemented
+    // this was added to test reading list
+    private vppL2Fib[] getL2Fibs(final int bdId) {
+        if (bdId == 0) {
+            return new vppL2Fib[]{
+                    new vppL2Fib(new byte[]{1, 2, 3, 4, 5, 6}, true, "ifc1", true, true)
+            };
+        } else {
+            return new vppL2Fib[]{
+                    new vppL2Fib(new byte[]{1, 2, 3, 4, 5, 6}, true, "ifc1", true, true),
+                    new vppL2Fib(new byte[]{2, 2, 3, 4, 5, 6}, true, "ifc2", true, true),
+            };
+        }
+    }
+
     private static String getMacAddress(byte[] mac) {
         StringBuilder sb = new StringBuilder(18);
         for (byte b : mac) {
index 782a7b6..5ac1f01 100644 (file)
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.MockitoAnnotations.initMocks;
 
 import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -41,14 +42,16 @@ public class VppDataBrokerInitializationProviderTest {
     private DataBroker bindingBroker;
     @Mock
     private WriteTransaction writeTx;
+    @Mock
+    private ReaderRegistry readerRegistry;
 
-    private VppDataBrokerInitializationProvider provider;
 
+    private VppDataBrokerInitializationProvider provider;
     @Before
     public void setUp() throws Exception {
         initMocks(this);
         doReturn(writeTx).when(bindingBroker).newWriteOnlyTransaction();
-        provider = new VppDataBrokerInitializationProvider(bindingBroker);
+        provider = new VppDataBrokerInitializationProvider(bindingBroker, readerRegistry);
     }
 
     @Test
index 9a33775..62ddf5c 100644 (file)
@@ -18,6 +18,7 @@ package io.fd.honeycomb.v3po.impl.data;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.TestCase.assertTrue;
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -26,7 +27,8 @@ import static org.mockito.MockitoAnnotations.initMocks;
 
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
-import io.fd.honeycomb.v3po.impl.trans0.VppReader;
+import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
+import java.util.Collections;
 import java.util.Map;
 import org.junit.Before;
 import org.junit.Test;
@@ -35,15 +37,19 @@ import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class VppOperationalDataTreeTest {
 
     @Mock
     private BindingNormalizedNodeSerializer serializer;
     @Mock
-    private VppReader reader;
+    private ReaderRegistry reader;
 
     private VppOperationalDataTree operationalData;
 
@@ -51,24 +57,32 @@ public class VppOperationalDataTreeTest {
     private InstanceIdentifier<DataObject> id;
     @Mock
     private Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry;
+    @Mock
+    private SchemaContext globalContext;
+    @Mock
+    private DataSchemaNode schemaNode;
 
     @Before
     public void setUp() {
         initMocks(this);
-        operationalData = new VppOperationalDataTree(serializer, reader);
+        operationalData = new VppOperationalDataTree(serializer, globalContext, reader);
     }
 
     @Test
     public void testRead() throws Exception {
         final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
+        final YangInstanceIdentifier.PathArgument pArg = mock(YangInstanceIdentifier.PathArgument.class);
+        doReturn(pArg).when(yangId).getLastPathArgument();
 
+        doReturn(QName.create("namespace", "2012-12-12", "local")).when(pArg).getNodeType();
+        doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class));
         doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
 
         final DataObject dataObject = mock(DataObject.class);
-        when(reader.read(id)).thenReturn(dataObject);
+        doReturn(Collections.singletonList(dataObject)).when(reader).read(id);
 
         when(serializer.toNormalizedNode(id, dataObject)).thenReturn(entry);
-        final NormalizedNode<?, ?> expectedValue = mock(NormalizedNode.class);
+        final DataContainerChild<?, ?> expectedValue = mock(DataContainerChild.class);
         doReturn(expectedValue).when(entry).getValue();
 
         final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = operationalData.read(yangId);
index 66831c7..cdb3024 100644 (file)
@@ -22,6 +22,7 @@ import static org.mockito.Matchers.anyString;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
 import io.fd.honeycomb.v3po.impl.trans.r.VppReader;
 import io.fd.honeycomb.v3po.impl.trans.r.impl.CompositeRootVppReader;
 import io.fd.honeycomb.v3po.impl.trans.r.util.DelegatingReaderRegistry;
@@ -140,9 +141,9 @@ public class VppStateTest {
 
     @Test
     public void testReadAll() throws Exception {
-        final List<? extends DataObject> dataObjects = readerRegistry.readAll();
+        final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects = readerRegistry.readAll();
         assertEquals(dataObjects.size(), 1);
-        final DataObject dataObject = dataObjects.get(0);
+        final DataObject dataObject = Iterables.getOnlyElement(dataObjects.get(Iterables.getOnlyElement(dataObjects.keySet())));
         assertTrue(dataObject instanceof VppState);
         assertVersion((VppState) dataObject);
         assertEquals(2, ((VppState) dataObject).getBridgeDomains().getBridgeDomain().size());