From 199494d2912ef7809d09f5f3131ff7f55e98f922 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Tue, 29 Mar 2016 13:37:36 +0200 Subject: VPP API <-> YANG translation layer integration Prototype that uses Readers API for non-list nodes. Change-Id: I482dcfe09d094456b014e55edbc38ce6e98afcc3 Signed-off-by: Marek Gradzki Signed-off-by: Maros Marsalek --- .../io/fd/honeycomb/v3po/impl/V3poProvider.java | 8 +- .../v3po/impl/data/ReadableVppDataTree.java | 3 +- .../v3po/impl/data/VppConfigDataTree.java | 8 +- .../data/VppDataBrokerInitializationProvider.java | 33 +++-- .../v3po/impl/data/VppOperationalDataTree.java | 149 ++++++++++++++++++--- .../v3po/impl/data/VppReaderRegistry.java | 112 ++++++++++++++++ .../v3po/impl/trans/r/ReaderRegistry.java | 12 +- .../trans/r/util/DelegatingReaderRegistry.java | 13 +- .../v3po/impl/vppstate/BridgeDomainCustomizer.java | 21 ++- .../VppDataBrokerInitializationProviderTest.java | 7 +- .../v3po/impl/data/VppOperationalDataTreeTest.java | 24 +++- .../honeycomb/v3po/impl/vppstate/VppStateTest.java | 5 +- 12 files changed, 338 insertions(+), 57 deletions(-) create mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java (limited to 'v3po/impl') diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java index 54acce3f2..8a0dead0e 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java @@ -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); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/ReadableVppDataTree.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/ReadableVppDataTree.java index 18e854a81..19248ddf0 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/ReadableVppDataTree.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/ReadableVppDataTree.java @@ -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>, ReadFailedException> read(YangInstanceIdentifier path); + CheckedFuture>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java index 66bcf489d..41a68e7bb 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppConfigDataTree.java @@ -136,7 +136,11 @@ public final class VppConfigDataTree implements VppDataTree { @Override public CheckedFuture>, ReadFailedException> read( final YangInstanceIdentifier path) { - return Futures.immediateCheckedFuture(snapshot.readNode(path)); + final Optional> 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> iterator = processedNodes.listIterator(processedNodes.size()); diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java index 50181e2fb..b7a24ea90 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java @@ -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 mountPointPath; private ObjectRegistration 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 writeFuture = Futures.transform( - readTx.read(LogicalDatastoreType.OPERATIONAL, interfacesID), + readTx.read(LogicalDatastoreType.OPERATIONAL, id), new AsyncFunction>, Void>() { @Override public ListenableFuture apply(final Optional> 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 diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java index fbfd9104d..c378365f3 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java @@ -16,65 +16,170 @@ 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>, 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.>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 dataObjects = readerRegistry.read(path); + + if (dataObjects.isEmpty()) { + return Futures.immediateCheckedFuture(Optional.>absent()); + } + + final NormalizedNode value = wrapDataObjects(yangInstanceIdentifier, path, dataObjects); + return Futures.immediateCheckedFuture(Optional.>fromNullable(value)); } - private Optional> toNormalizedNode(final InstanceIdentifier path, - final DataObject dataObject) { - LOG.trace("VppOperationalDataProxy.toNormalizedNode(), path={}, path={}", path, dataObject); - final Map.Entry> entry = - serializer.toNormalizedNode(path, dataObject); + private DataSchemaNode getSchemaNode(final @Nonnull YangInstanceIdentifier yangInstanceIdentifier) { + return globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType()); + } + + private NormalizedNode readRoot() { + final DataContainerNodeAttrBuilder dataNodeBuilder = + Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME)); + + final Multimap, ? extends DataObject> dataObjects = + readerRegistry.readAll(); + + for (final InstanceIdentifier 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 instanceIdentifier, + final Collection dataObjects) { + final Collection> 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> normalizedRootElements, final ListSchemaNode listSchema) { + if (listSchema.getKeyDefinition().isEmpty()) { + final CollectionNodeBuilder listBuilder = + Builders.unkeyedListBuilder(); + for (NormalizedNode normalizedRootElement : normalizedRootElements) { + listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement); + } + return listBuilder.build(); + } else { + final CollectionNodeBuilder 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> toNormalizedNodeFunction(final InstanceIdentifier path) { + return new Function>() { + @Override + public NormalizedNode apply(@Nullable final DataObject dataObject) { + LOG.trace("VppOperationalDataProxy.toNormalizedNode(), path={}, dataObject={}", path, dataObject); + final Map.Entry> entry = + serializer.toNormalizedNode(path, dataObject); - final Optional> optional = Optional.>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 index 000000000..c5d4a8194 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppReaderRegistry.java @@ -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 vppStateReader = initVppStateReader(vppApi); + // TODO add more root readers + reader = new DelegatingReaderRegistry(Collections.>singletonList(vppStateReader)); + } + + private static CompositeRootVppReader initVppStateReader(@Nonnull final vppApi vppApi) { + + final ChildVppReader versionReader = new CompositeChildVppReader<>( + Version.class, new VersionCustomizer(vppApi)); + + final CompositeListVppReader + bridgeDomainReader = new CompositeListVppReader<>( + BridgeDomain.class, + new BridgeDomainCustomizer(vppApi)); + + final ChildVppReader bridgeDomainsReader = new CompositeChildVppReader<>( + BridgeDomains.class, + VppRWUtils.singletonChildReaderList(bridgeDomainReader), + new ReflexiveChildReaderCustomizer<>(BridgeDomainsBuilder.class)); + + final List>> childVppReaders = new ArrayList<>(); + childVppReaders.add(versionReader); + childVppReaders.add(bridgeDomainsReader); + + return new CompositeRootVppReader<>( + VppState.class, + childVppReaders, + VppRWUtils.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, ? extends DataObject> readAll() { + return reader.readAll(); + } + + @Nonnull + @Override + public List read(@Nonnull final InstanceIdentifier id) { + return reader.read(id); + } + + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + return reader.getManagedDataObjectType(); + } +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java index 49705fe3b..8c592a699 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/ReaderRegistry.java @@ -17,19 +17,23 @@ 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 { /** - * 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 readAll(); + Multimap, ? extends DataObject> readAll(); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java index 033e01b09..4e50e5aa8 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/r/util/DelegatingReaderRegistry.java @@ -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 readAll() { - LOG.debug("Reading from all delegates"); + public Multimap, ? extends DataObject> readAll() { + LOG.debug("Reading from all delegates: {}", this); LOG.trace("Reading from all delegates: {}", rootReaders.values()); - final List objects = new ArrayList<>(rootReaders.size()); + final Multimap, DataObject> objects = LinkedListMultimap.create(); for (VppReader rootReader : rootReaders.values()) { LOG.debug("Reading from delegate: {}", rootReader); final List read = rootReader.read(rootReader.getManagedDataObjectType()); - objects.addAll(read); + objects.putAll(rootReader.getManagedDataObjectType(), read); } return objects; } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java index 1930a83e5..07193a16c 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java @@ -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 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) { diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java index 782a7b6fe..5ac1f0130 100644 --- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java @@ -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 diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java index 9a3377563..62ddf5ca0 100644 --- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTreeTest.java @@ -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 id; @Mock private Map.Entry> 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>, ReadFailedException> future = operationalData.read(yangId); diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateTest.java index 66831c77a..cdb3024d8 100644 --- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateTest.java +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateTest.java @@ -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 dataObjects = readerRegistry.readAll(); + final Multimap, ? 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()); -- cgit 1.2.3-korg