From 355c2205a7088bc7b3ccabc278c477b838975c65 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Fri, 1 Apr 2016 12:33:39 +0200 Subject: VPP API <-> YANG translation layer integration #2(Writers) Change-Id: If91364f1900693b040aee163f1c6e092381b0efd Signed-off-by: Maros Marsalek Signed-off-by: Marek Gradzki --- .../io/fd/honeycomb/v3po/impl/V3poProvider.java | 4 +- .../v3po/impl/data/VppConfigDataTree.java | 102 +++++--- .../data/VppDataBrokerInitializationProvider.java | 17 +- .../fd/honeycomb/v3po/impl/data/VppDataTree.java | 2 +- .../v3po/impl/data/VppWriteTransaction.java | 2 +- .../v3po/impl/data/VppWriterRegistry.java | 92 +++++++ .../v3po/impl/trans/VppApiInvocationException.java | 76 ++++++ .../impl/trans/w/util/TransactionWriteContext.java | 49 ++-- .../v3po/impl/trans0/DefaultVppWriter.java | 43 ---- .../impl/trans0/VppApiInvocationException.java | 76 ------ .../v3po/impl/trans0/VppInterfacesReader.java | 56 ----- .../fd/honeycomb/v3po/impl/trans0/VppReader.java | 30 --- .../fd/honeycomb/v3po/impl/trans0/VppWriter.java | 27 --- .../v3po/impl/vpp/BridgeDomainCustomizer.java | 71 +++--- .../v3po/impl/data/VPPConfigDataTreeTest.java | 54 +++-- .../VppDataBrokerInitializationProviderTest.java | 5 +- .../impl/trans/VppApiInvocationExceptionTest.java | 46 ++++ .../trans/w/util/TransactionWriteContextTest.java | 122 ++++++++++ .../impl/trans0/VppApiInvocationExceptionTest.java | 46 ---- .../v3po/impl/vpp/BridgeDomainCustomizerTest.java | 269 +++++++++++++++++++++ .../v3po/impl/vpp/BridgeDomainTestUtils.java | 64 +++++ .../io/fd/honeycomb/v3po/impl/vpp/VppTest.java | 13 +- 22 files changed, 870 insertions(+), 396 deletions(-) create mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java create mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java delete mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/DefaultVppWriter.java delete mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppApiInvocationException.java delete mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppInterfacesReader.java delete mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppReader.java delete mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppWriter.java create mode 100644 v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationExceptionTest.java create mode 100644 v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContextTest.java delete mode 100644 v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans0/VppApiInvocationExceptionTest.java create mode 100644 v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainCustomizerTest.java create mode 100644 v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainTestUtils.java (limited to 'v3po/impl/src') 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 4afd86099..931ecefd6 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 @@ -22,6 +22,7 @@ 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 io.fd.honeycomb.v3po.impl.data.VppWriterRegistry; import java.io.IOException; import java.util.Collections; import java.util.HashMap; @@ -196,9 +197,10 @@ public class V3poProvider implements BindingAwareProvider, AutoCloseable, Broker startOperationalUpdateTimer(); final VppReaderRegistry readerRegistry = VppReaderRegistry.getInstance(api); + final VppWriterRegistry writerRegistry = VppWriterRegistry.getInstance(api); // TODO make configurable: - vppDataBrokerInitializationProvider = new VppDataBrokerInitializationProvider(db, readerRegistry); + vppDataBrokerInitializationProvider = new VppDataBrokerInitializationProvider(db, readerRegistry, writerRegistry); // TODO pull the registration into Module domBroker.registerProvider(vppDataBrokerInitializationProvider); } 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 41a68e7bb..6fdeea348 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 @@ -16,12 +16,15 @@ package io.fd.honeycomb.v3po.impl.data; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.singletonList; + import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; -import io.fd.honeycomb.v3po.impl.trans0.VppApiInvocationException; -import io.fd.honeycomb.v3po.impl.trans0.VppWriter; +import io.fd.honeycomb.v3po.impl.trans.w.WriteContext; +import io.fd.honeycomb.v3po.impl.trans.w.util.TransactionWriteContext; +import io.fd.honeycomb.v3po.impl.trans.VppApiInvocationException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -31,6 +34,7 @@ import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -55,7 +59,14 @@ public final class VppConfigDataTree implements VppDataTree { private final BindingNormalizedNodeSerializer serializer; private final DataTree dataTree; - private final VppWriter writer; + private final VppWriterRegistry writer; + public static final ReadableVppDataTree EMPTY_OPERATIONAL = new ReadableVppDataTree() { + @Override + public CheckedFuture>, ReadFailedException> read( + @Nonnull final YangInstanceIdentifier path) { + return Futures.immediateCheckedFuture(Optional.>absent()); + } + }; /** * Creates configuration data tree instance. @@ -66,10 +77,10 @@ public final class VppConfigDataTree implements VppDataTree { * @param vppWriter service for translation between Java Binding Data and Vpp. */ public VppConfigDataTree(@Nonnull final BindingNormalizedNodeSerializer serializer, - @Nonnull final DataTree dataTree, @Nonnull final VppWriter vppWriter) { - this.serializer = Preconditions.checkNotNull(serializer, "serializer should not be null"); - this.dataTree = Preconditions.checkNotNull(dataTree, "dataTree should not be null"); - this.writer = Preconditions.checkNotNull(vppWriter, "vppWriter should not be null"); + @Nonnull final DataTree dataTree, @Nonnull final VppWriterRegistry vppWriter) { + this.serializer = checkNotNull(serializer, "serializer should not be null"); + this.dataTree = checkNotNull(dataTree, "dataTree should not be null"); + this.writer = checkNotNull(vppWriter, "vppWriter should not be null"); } @Override @@ -78,7 +89,8 @@ public final class VppConfigDataTree implements VppDataTree { } @Override - public void commit(final DataTreeModification modification) throws DataValidationFailedException, VppApiInvocationException { + public void commit(final DataTreeModification modification) + throws DataValidationFailedException, VppApiInvocationException { dataTree.validate(modification); final DataTreeCandidate candidate = dataTree.prepare(modification); @@ -87,16 +99,22 @@ public final class VppConfigDataTree implements VppDataTree { final YangInstanceIdentifier rootPath = candidate.getRootPath(); final Optional> normalizedDataBefore = rootNode.getDataBefore(); final Optional> normalizedDataAfter = rootNode.getDataAfter(); - LOG.debug("VppConfigDataProxy.commit() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", + LOG.debug("VppConfigDataTree.commit() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", rootPath, rootNode, normalizedDataBefore, normalizedDataAfter); final Map, DataObject> nodesBefore = extractNetconfData(normalizedDataBefore); - LOG.debug("VppConfigDataProxy.commit() extracted nodesBefore={}", nodesBefore.keySet()); + LOG.debug("VppConfigDataTree.commit() extracted nodesBefore={}", nodesBefore.keySet()); final Map, DataObject> nodesAfter = extractNetconfData(normalizedDataAfter); - LOG.debug("VppConfigDataProxy.commit() extracted nodesAfter={}", nodesAfter.keySet()); + LOG.debug("VppConfigDataTree.commit() extracted nodesAfter={}", nodesAfter.keySet()); - final ChangesProcessor processor = new ChangesProcessor(writer, nodesBefore, nodesAfter); + + final DOMDataReadOnlyTransaction beforeTx = new VppReadOnlyTransaction(EMPTY_OPERATIONAL, takeSnapshot()); + final ConfigSnapshot modificationSnapshot = new ConfigSnapshot(modification); + final DOMDataReadOnlyTransaction afterTx = new VppReadOnlyTransaction(EMPTY_OPERATIONAL, modificationSnapshot); + final WriteContext ctx = new TransactionWriteContext(serializer, beforeTx, afterTx); + + final ChangesProcessor processor = new ChangesProcessor(writer, nodesBefore, nodesAfter, ctx); try { processor.applyChanges(); } catch (VppApiInvocationException e) { @@ -118,9 +136,10 @@ public final class VppConfigDataTree implements VppDataTree { dataTree.commit(candidate); } - private Map, DataObject> extractNetconfData(final Optional> parentOptional) { + private Map, DataObject> extractNetconfData( + final Optional> parentOptional) { if (parentOptional.isPresent()) { - final DataContainerNode parent = (DataContainerNode)parentOptional.get(); + final DataContainerNode parent = (DataContainerNode) parentOptional.get(); return DataTreeUtils.childrenFromNormalized(parent, serializer); } return Collections.emptyMap(); @@ -135,7 +154,7 @@ public final class VppConfigDataTree implements VppDataTree { @Override public CheckedFuture>, ReadFailedException> read( - final YangInstanceIdentifier path) { + @Nonnull final YangInstanceIdentifier path) { final Optional> node = snapshot.readNode(path); if (LOG.isTraceEnabled() && node.isPresent()) { LOG.trace("ConfigSnapshot.read: {}", node.get()); @@ -150,57 +169,80 @@ public final class VppConfigDataTree implements VppDataTree { } private static final class ChangesProcessor { - private final VppWriter writer; + private final VppWriterRegistry writer; private final List> processedNodes; private final Map, DataObject> nodesBefore; private final Map, DataObject> nodesAfter; + private final WriteContext ctx; - ChangesProcessor(@Nonnull final VppWriter writer, + ChangesProcessor(@Nonnull final VppWriterRegistry writer, final Map, DataObject> nodesBefore, - final Map, DataObject> nodesAfter) { - this.writer = Preconditions.checkNotNull(writer, "VppWriter is null!"); - this.nodesBefore = Preconditions.checkNotNull(nodesBefore, "nodesBefore is null!"); - this.nodesAfter = Preconditions.checkNotNull(nodesAfter, "nodesAfter is null!"); + final Map, DataObject> nodesAfter, + @Nonnull final WriteContext writeContext) { + this.ctx = checkNotNull(writeContext, "writeContext is null!"); + this.writer = checkNotNull(writer, "VppWriter is null!"); + this.nodesBefore = checkNotNull(nodesBefore, "nodesBefore is null!"); + this.nodesAfter = checkNotNull(nodesAfter, "nodesAfter is null!"); processedNodes = new ArrayList<>(); } void applyChanges() throws VppApiInvocationException { // TODO we should care about the order of modified subtrees + // TODO maybe WriterRegistry could provide writeAll method and it will process the updates + // in order in which it child writers are registered final Set> allNodes = new HashSet<>(); allNodes.addAll(nodesBefore.keySet()); allNodes.addAll(nodesAfter.keySet()); - LOG.debug("VppConfigDataProxy.applyChanges() all extracted nodes: {}", allNodes); + LOG.debug("ChangesProcessor.applyChanges() all extracted nodes: {}", allNodes); for (InstanceIdentifier node : allNodes) { - LOG.debug("VppConfigDataProxy.applyChanges() processing node={}", node); + LOG.debug("ChangesProcessor.applyChanges() processing node={}", node); final DataObject dataBefore = nodesBefore.get(node); final DataObject dataAfter = nodesAfter.get(node); + LOG.debug("ChangesProcessor.applyChanges() processing dataBefore={}, dataAfter={}", dataBefore, + dataAfter); try { - writer.process(dataBefore, dataAfter); + // TODO is List as input argument really necessary for writer ? + final List dataObjectsBefore = dataBefore == null + ? Collections.emptyList() + : singletonList(dataBefore); + final List dataObjectsAfter = dataAfter == null + ? Collections.emptyList() + : singletonList(dataAfter); + LOG.debug("ChangesProcessor.applyChanges() processing dataObjectsBefore={}, dataObjectsAfter={}", + dataObjectsBefore, dataObjectsAfter); + writer.update(node, dataObjectsBefore, dataObjectsAfter, ctx); processedNodes.add(node); - } catch (VppApiInvocationException e) { + } catch (RuntimeException e) { LOG.error("Error while processing data change (before={}, after={})", dataBefore, dataAfter, e); - throw e; + // FIXME ex handling + throw new VppApiInvocationException("", 1, -1); } } } void revertChanges() throws VppApiInvocationException { - Preconditions.checkNotNull(writer, "VppWriter is null!"); + checkNotNull(writer, "VppWriter is null!"); // revert changes in reverse order they were applied final ListIterator> iterator = processedNodes.listIterator(processedNodes.size()); while (iterator.hasPrevious()) { final InstanceIdentifier node = iterator.previous(); - LOG.debug("VppConfigDataProxy.revertChanges() processing node={}", node); + LOG.debug("ChangesProcessor.revertChanges() processing node={}", node); final DataObject dataBefore = nodesBefore.get(node); final DataObject dataAfter = nodesAfter.get(node); // revert a change by invoking writer with reordered arguments - writer.process(dataAfter, dataBefore); + try { + // TODO is List as input argument really necessary for writer ? + writer.update(node, singletonList(dataAfter), singletonList(dataBefore), ctx); + } catch (RuntimeException e) { + // FIXME ex handling + throw new VppApiInvocationException("", 1, -1); + } } } } 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 129a1b84b..2a14f48b1 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 @@ -16,6 +16,8 @@ package io.fd.honeycomb.v3po.impl.data; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.AsyncFunction; @@ -23,7 +25,6 @@ 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 java.util.Collection; import java.util.Collections; import javassist.ClassPool; @@ -84,12 +85,16 @@ public final class VppDataBrokerInitializationProvider implements Provider, Auto private final DataBroker bindingBroker; private final ReaderRegistry readerRegistry; private final InstanceIdentifier mountPointPath; + private final VppWriterRegistry writerRegistry; private ObjectRegistration mountPointRegistration; private DOMDataBroker broker; - 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"); + public VppDataBrokerInitializationProvider(@Nonnull final DataBroker bindingBroker, + final ReaderRegistry readerRegistry, + final VppWriterRegistry writerRegistry) { + this.bindingBroker = checkNotNull(bindingBroker, "bindingBroker should not be null"); + this.readerRegistry = checkNotNull(readerRegistry, "readerRegistry should not be null"); + this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null"); this.mountPointPath = getMountPointPath(); } @@ -180,9 +185,7 @@ public final class VppDataBrokerInitializationProvider implements Provider, Auto 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 + final VppDataTree configDataProxy = new VppConfigDataTree(serializer, dataTree, writerRegistry); // TODO make configurable return new VppDataBroker(operationalData, configDataProxy); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataTree.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataTree.java index 17d3b7149..a3a4fae65 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataTree.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataTree.java @@ -17,7 +17,7 @@ package io.fd.honeycomb.v3po.impl.data; import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.impl.trans0.VppApiInvocationException; +import io.fd.honeycomb.v3po.impl.trans.VppApiInvocationException; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriteTransaction.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriteTransaction.java index 8cdd10b7e..b7aa2f854 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriteTransaction.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriteTransaction.java @@ -26,7 +26,7 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import io.fd.honeycomb.v3po.impl.trans0.VppApiInvocationException; +import io.fd.honeycomb.v3po.impl.trans.VppApiInvocationException; import javax.annotation.Nonnull; import javax.annotation.concurrent.NotThreadSafe; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java new file mode 100644 index 000000000..67b45cfd2 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppWriterRegistry.java @@ -0,0 +1,92 @@ +/* + * 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 io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils; +import io.fd.honeycomb.v3po.impl.trans.w.ChildVppWriter; +import io.fd.honeycomb.v3po.impl.trans.w.VppWriter; +import io.fd.honeycomb.v3po.impl.trans.w.WriteContext; +import io.fd.honeycomb.v3po.impl.trans.w.impl.CompositeChildVppWriter; +import io.fd.honeycomb.v3po.impl.trans.w.impl.CompositeListVppWriter; +import io.fd.honeycomb.v3po.impl.trans.w.impl.CompositeRootVppWriter; +import io.fd.honeycomb.v3po.impl.trans.w.util.DelegatingWriterRegistry; +import io.fd.honeycomb.v3po.impl.trans.w.util.NoopWriterCustomizer; +import io.fd.honeycomb.v3po.impl.trans.w.util.ReflexiveChildWriterCustomizer; +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.Vpp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.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 VppWriterRegistry implements VppWriter { + + private static VppWriterRegistry instance; + + private final DelegatingWriterRegistry writer; + + private VppWriterRegistry(@Nonnull final vppApi vppApi) { + final CompositeRootVppWriter vppWriter = initVppStateWriter(vppApi); + writer = new DelegatingWriterRegistry(Collections.>singletonList(vppWriter)); + } + + private static CompositeRootVppWriter initVppStateWriter(@Nonnull final vppApi vppApi) { + final CompositeListVppWriter bridgeDomainWriter = new CompositeListVppWriter<>( + BridgeDomain.class, + new io.fd.honeycomb.v3po.impl.vpp.BridgeDomainCustomizer(vppApi)); + + final ChildVppWriter bridgeDomainsWriter = new CompositeChildVppWriter<>( + BridgeDomains.class, + VppRWUtils.singletonChildWriterList(bridgeDomainWriter), + new ReflexiveChildWriterCustomizer()); + + final List>> childWriters = new ArrayList<>(); + childWriters.add(bridgeDomainsWriter); + + return new CompositeRootVppWriter<>( + Vpp.class, + childWriters, + new NoopWriterCustomizer()); + } + + public static synchronized VppWriterRegistry getInstance(@Nonnull final vppApi vppApi) { + if (instance == null) { + instance = new VppWriterRegistry(vppApi); + } + return instance; + } + + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + return writer.getManagedDataObjectType(); + } + + @Override + public void update(@Nonnull final InstanceIdentifier id, + @Nonnull final List dataBefore, + @Nonnull final List data, @Nonnull final WriteContext ctx) { + writer.update(id, dataBefore, data, ctx); + } +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java new file mode 100644 index 000000000..b0076cd9c --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationException.java @@ -0,0 +1,76 @@ +/* + * 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.trans; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import javax.annotation.Nonnull; + +/** + * Throws when Vpp jAPI method invocation failed. + */ +@Beta +public class VppApiInvocationException extends Exception { + private final String methodName; + private final int ctxId; + private final int errorCode; + + /** + * Constructs an VppApiInvocationFailedException with the specified api method name and error code. + * + * @param methodName method name that failed to invoke + * @param ctxId api request context identifier + * @param errorCode negative error code value associated with this failure + * @throws NullPointerException if apiMethodName is null + * @throws IllegalArgumentException if errorCode is nonnegative + */ + public VppApiInvocationException(@Nonnull final String methodName, final int ctxId, final int errorCode) { + super(String.format("vppApi.%s failed with error code: %d (ctxId=%d) ", methodName, errorCode, ctxId)); + this.methodName = Preconditions.checkNotNull(methodName, "apiMethodName is null!"); + this.ctxId = ctxId; + Preconditions.checkArgument(errorCode < 0); + this.errorCode = errorCode; + } + + /** + * Returns method name that failed to invoke. + * + * @return method name + */ + public String getMethodName() { + return methodName; + } + + /** + * Returns api request context identifier. + * + * @return value of context identifier + */ + public int getCtxId() { + return ctxId; + } + + /** + * Returns the error code associated with this failure. + * + * @return a negative integer error code + */ + public int getErrorCode() { + return errorCode; + } +} + diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContext.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContext.java index 3a1fd2f57..7c1a63da6 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContext.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContext.java @@ -22,29 +22,30 @@ import io.fd.honeycomb.v3po.impl.trans.util.Context; import io.fd.honeycomb.v3po.impl.trans.w.WriteContext; import java.util.Collections; import java.util.List; -import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import java.util.Map; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +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.NormalizedNode; public class TransactionWriteContext implements WriteContext, AutoCloseable { - private final ReadOnlyTransaction beforeTx; - private final ReadOnlyTransaction afterTx; + private final DOMDataReadOnlyTransaction beforeTx; + private final DOMDataReadOnlyTransaction afterTx; private final Context ctx; + private final BindingNormalizedNodeSerializer serializer; - public TransactionWriteContext(final ReadOnlyTransaction beforeTx, final ReadOnlyTransaction afterTx, - final Context ctx) { - super(); + public TransactionWriteContext(final BindingNormalizedNodeSerializer serializer, + final DOMDataReadOnlyTransaction beforeTx, + final DOMDataReadOnlyTransaction afterTx) { + this.serializer = serializer; this.beforeTx = beforeTx; this.afterTx = afterTx; - this.ctx = ctx; - } - - public TransactionWriteContext(final ReadOnlyTransaction beforeTx, - final ReadOnlyTransaction afterTx) { - this(beforeTx, afterTx, new Context()); + this.ctx = new Context(); } @Override @@ -53,16 +54,26 @@ public class TransactionWriteContext implements WriteContext, AutoCloseable { } private List read(final InstanceIdentifier currentId, - final ReadOnlyTransaction tx) { + final DOMDataReadOnlyTransaction tx) { // FIXME how to read all for list (using wildcarded ID) ? - final CheckedFuture, ReadFailedException> read = - tx.read(LogicalDatastoreType.CONFIGURATION, currentId); + final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(currentId); + + final CheckedFuture>, ReadFailedException> read = + tx.read(LogicalDatastoreType.CONFIGURATION, path); + try { - final Optional optional = read.checkedGet(); - return optional.isPresent() - ? Collections.singletonList(optional.get()) - : Collections.emptyList(); + final Optional> optional = read.checkedGet(); + + if (!optional.isPresent()) { + return Collections.emptyList(); + } + + final NormalizedNode data = optional.get(); + final Map.Entry, DataObject> entry = + serializer.fromNormalizedNode(path, data); + + return Collections.singletonList(entry.getValue()); } catch (ReadFailedException e) { throw new IllegalStateException("Unable to perform read", e); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/DefaultVppWriter.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/DefaultVppWriter.java deleted file mode 100644 index 573353b55..000000000 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/DefaultVppWriter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.trans0; - -import java.util.Objects; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DefaultVppWriter implements VppWriter { - private static final Logger LOG = LoggerFactory.getLogger(DefaultVppWriter.class); - - @Override - public void process(@Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter) - throws VppApiInvocationException { - LOG.debug("Processing modification: dataBefore={}, dataAfter={}", dataBefore, dataAfter); - - if (Objects.equals(dataBefore, dataAfter)) { - LOG.debug("No modification"); - } else if (dataBefore == null) { - LOG.debug("modification type: CREATE"); - } else if (dataAfter == null) { - LOG.debug("modification type: DELETE"); - } else { - LOG.debug("modification type: UPDATE"); - } - } -} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppApiInvocationException.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppApiInvocationException.java deleted file mode 100644 index 119563bdd..000000000 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppApiInvocationException.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.trans0; - -import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; -import javax.annotation.Nonnull; - -/** - * Throws when Vpp jAPI method invocation failed. - */ -@Beta -public class VppApiInvocationException extends Exception { - private final String methodName; - private final int ctxId; - private final int errorCode; - - /** - * Constructs an VppApiInvocationFailedException with the specified api method name and error code. - * - * @param methodName method name that failed to invoke - * @param ctxId api request context identifier - * @param errorCode negative error code value associated with this failure - * @throws NullPointerException if apiMethodName is null - * @throws IllegalArgumentException if errorCode is nonnegative - */ - public VppApiInvocationException(@Nonnull final String methodName, final int ctxId, final int errorCode) { - super(String.format("vppApi.%s failed with error code: %d (ctxId=%d) ", methodName, errorCode, ctxId)); - this.methodName = Preconditions.checkNotNull(methodName, "apiMethodName is null!"); - this.ctxId = ctxId; - Preconditions.checkArgument(errorCode < 0); - this.errorCode = errorCode; - } - - /** - * Returns method name that failed to invoke. - * - * @return method name - */ - public String getMethodName() { - return methodName; - } - - /** - * Returns api request context identifier. - * - * @return value of context identifier - */ - public int getCtxId() { - return ctxId; - } - - /** - * Returns the error code associated with this failure. - * - * @return a negative integer error code - */ - public int getErrorCode() { - return errorCode; - } -} - diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppInterfacesReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppInterfacesReader.java deleted file mode 100644 index 39b5f0208..000000000 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppInterfacesReader.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.trans0; - -import java.util.Collections; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class VppInterfacesReader implements VppReader { - private static final Logger LOG = LoggerFactory.getLogger(VppInterfacesReader.class); - - @Override - public Interfaces read(final InstanceIdentifier id) { - LOG.info("VppInterfacesReader.read, id={}", id); - - InterfaceBuilder ifaceBuilder = new InterfaceBuilder(); - final String interfaceName = "eth0"; - ifaceBuilder.setName(interfaceName); - ifaceBuilder.setDescription("eth0 description"); - ifaceBuilder.setEnabled(false); - ifaceBuilder.setKey(new InterfaceKey(interfaceName)); - ifaceBuilder.setType(EthernetCsmacd.class); - ifaceBuilder.setLinkUpDownTrapEnable(Interface.LinkUpDownTrapEnable.Disabled); - - InterfacesBuilder ifacesBuilder = new InterfacesBuilder(); - ifacesBuilder.setInterface(Collections.singletonList(ifaceBuilder.build())); - return ifacesBuilder.build(); - } - - @Override - public Class getManagedDataObjectType() { - return null; - } -} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppReader.java deleted file mode 100644 index 5a4215edb..000000000 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppReader.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.trans0; - -import com.google.common.annotations.Beta; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -@Beta -public interface VppReader { - - C read(InstanceIdentifier id); - - Class getManagedDataObjectType(); - -} \ No newline at end of file diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppWriter.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppWriter.java deleted file mode 100644 index ee6e1f4be..000000000 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans0/VppWriter.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.trans0; - -import com.google.common.annotations.Beta; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.DataObject; - -@Beta -public interface VppWriter { - - void process(@Nullable C dataBefore, @Nullable C dataAfter) throws VppApiInvocationException; -} \ No newline at end of file diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainCustomizer.java index f707164eb..c56ebb1a8 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainCustomizer.java @@ -16,6 +16,8 @@ package io.fd.honeycomb.v3po.impl.vpp; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import io.fd.honeycomb.v3po.impl.trans.util.Context; @@ -38,9 +40,9 @@ public class BridgeDomainCustomizer private static final Logger LOG = LoggerFactory.getLogger(BridgeDomainCustomizer.class); - static final byte ADD_BD = (byte) 1; - static final int NO_RET_VAL = -77; - static final int RELEASE = 1; + private static final byte ADD_OR_UPDATE_BD = (byte) 1; + private static final int RESPONSE_NOT_READY = -77; + private static final int RELEASE = 1; public BridgeDomainCustomizer(final org.openvpp.vppjapi.vppApi api) { super(api); @@ -53,30 +55,37 @@ public class BridgeDomainCustomizer return ((BridgeDomains) parentData).getBridgeDomain(); } + private int waitForResponse(final int ctxId) { + int rv; + while ((rv = getVppApi().getRetval(ctxId, RELEASE)) == RESPONSE_NOT_READY) { + // TODO limit attempts + } + return rv; + } + + private int addOrUpdateBridgeDomain(final int bdId, @Nonnull final BridgeDomain bd) { + byte flood = booleanToByte(bd.isFlood()); + byte forward = booleanToByte(bd.isForward()); + byte learn = booleanToByte(bd.isLearn()); + byte uuf = booleanToByte(bd.isUnknownUnicastFlood()); + byte arpTerm = booleanToByte(bd.isArpTermination()); + + int ctxId = getVppApi().bridgeDomainAddDel(bdId, flood, forward, learn, uuf, arpTerm, ADD_OR_UPDATE_BD); + return waitForResponse(ctxId); + } + @Override public void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final BridgeDomain current, @Nonnull final Context ctx) { + LOG.debug("writeCurrentAttributes: id={}, current={}, ctx={}", id, current, ctx); final String bdName = current.getName(); int bdId = getVppApi().findOrAddBridgeDomainId(bdName); checkState(bdId > 0, "Unable to find or create bridge domain. Return code: %s", bdId); - byte flood = booleanToByte(current.isFlood()); - byte forward = booleanToByte(current.isForward()); - byte learn = booleanToByte(current.isLearn()); - byte uuf = booleanToByte(current.isUnknownUnicastFlood()); - byte arpTerm = booleanToByte(current.isArpTermination()); - - int ctxId = getVppApi().bridgeDomainAddDel(bdId, flood, forward, learn, uuf, arpTerm, ADD_BD); - - int rv = NO_RET_VAL; - while (rv == -77) { - rv = getVppApi().getRetval(ctxId, RELEASE /* release */); - // TODO limit attempts - } - checkState(rv > 0, "Bridge domain %s(%s) write failed. Return code: %s", bdName, bdId, rv); + int rv = addOrUpdateBridgeDomain(bdId, current); - bdId = getVppApi().bridgeDomainIdFromName(bdName); + checkState(rv >= 0, "Bridge domain %s(%s) write failed. Return code: %s", bdName, bdId, rv); LOG.debug("Bridge domain {} written as {} successfully", bdName, bdId); } @@ -88,9 +97,10 @@ public class BridgeDomainCustomizer public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final BridgeDomain dataBefore, @Nonnull final Context ctx) { + LOG.debug("deleteCurrentAttributes: id={}, dataBefore={}, ctx={}", id, dataBefore, ctx); String bdName = id.firstKeyOf(BridgeDomain.class).getName(); - int bdId = getVppApi().findOrAddBridgeDomainId(bdName); + int bdId = getVppApi().bridgeDomainIdFromName(bdName); checkState(bdId > 0, "Unable to delete bridge domain. Does not exist. Return code: %s", bdId); int ctxId = getVppApi().bridgeDomainAddDel(bdId, @@ -101,13 +111,9 @@ public class BridgeDomainCustomizer (byte) 0 /* arpTerm */, (byte) 0 /* isAdd */); - int rv = NO_RET_VAL; - while (rv == NO_RET_VAL) { - rv = getVppApi().getRetval(ctxId, RELEASE /* release */); - // TODO limit attempts - } + int rv = waitForResponse(ctxId); - checkState(rv > 0, "Bridge domain delete failed. Return code: %s", rv); + checkState(rv >= 0, "Bridge domain delete failed. Return code: %s", rv); LOG.debug("Bridge domain {} deleted as {} successfully", bdName, bdId); } @@ -115,9 +121,18 @@ public class BridgeDomainCustomizer public void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final BridgeDomain dataBefore, @Nonnull final BridgeDomain dataAfter, @Nonnull final Context ctx) { - // Most basic update implementation: Delete + Write - deleteCurrentAttributes(id, dataBefore, ctx); - writeCurrentAttributes(id, dataAfter, ctx); + LOG.debug("updateCurrentAttributes: id={}, dataBefore={}, dataAfter={}, ctx={}", id, dataBefore, dataAfter, ctx); + + final String bdName = checkNotNull(dataAfter.getName()); + checkArgument(bdName.equals(dataBefore.getName()), "BridgeDomain name changed. It should be deleted and then created."); + + int bdId = getVppApi().bridgeDomainIdFromName(bdName); + checkState(bdId > 0, "Unable to find bridge domain. Return code: %s", bdId); + + final int rv = addOrUpdateBridgeDomain(bdId, dataAfter); + + checkState(rv >= 0, "Bridge domain %s(%s) update failed. Return code: %s", bdName, bdId, rv); + LOG.debug("Bridge domain {}({}) updated successfully", bdName, bdId); } } diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java index 4e855f447..6aa9da57c 100644 --- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VPPConfigDataTreeTest.java @@ -16,10 +16,12 @@ package io.fd.honeycomb.v3po.impl.data; +import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyListOf; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; @@ -31,17 +33,17 @@ 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.VppApiInvocationException; -import io.fd.honeycomb.v3po.impl.trans0.VppWriter; +import io.fd.honeycomb.v3po.impl.trans.w.WriteContext; +import io.fd.honeycomb.v3po.impl.trans.VppApiInvocationException; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; +import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -67,7 +69,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailed public class VPPConfigDataTreeTest { @Mock - private VppWriter vppWriter; + private VppWriterRegistry vppWriter; @Mock private BindingNormalizedNodeSerializer serializer; @Mock @@ -135,7 +137,8 @@ public class VPPConfigDataTreeTest { proxy.commit(modification); // Verify all changes were processed: - verify(vppWriter).process(dataBefore, dataAfter); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(dataBefore)), + eq(singletonList(dataAfter)), any(WriteContext.class)); // Verify modification was validated verify(dataTree).validate(modification); @@ -155,8 +158,9 @@ public class VPPConfigDataTreeTest { final List> processedChanges = new ArrayList<>(); // reject third applied change - final Answer answer = new VppWriterAnswer(processedChanges, Arrays.asList(1,2), Collections.singletonList(3)); - doAnswer(answer).when(vppWriter).process(any(DataObject.class), any(DataObject.class)); + final Answer answer = new VppWriterAnswer(processedChanges, Arrays.asList(1,2), singletonList(3)); + doAnswer(answer).when(vppWriter).update(Matchers.>any(), anyListOf(DataObject.class), + anyListOf(DataObject.class), any(WriteContext.class)); // Prepare modification: final DataTreeCandidateNode rootNode = mockRootNode(); @@ -172,16 +176,20 @@ public class VPPConfigDataTreeTest { proxy.commit(modification); } catch (DataValidationFailedException | VppApiInvocationException e) { // verify that all changes were processed: - verify(vppWriter).process(dataBefore1, dataAfter1); - verify(vppWriter).process(dataBefore2, dataAfter2); - verify(vppWriter).process(dataBefore3, dataAfter3); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(dataBefore1)), + eq(singletonList(dataAfter1)), any(WriteContext.class)); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(dataBefore2)), + eq(singletonList(dataAfter2)), any(WriteContext.class)); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(dataBefore3)), + eq(singletonList(dataAfter3)), any(WriteContext.class)); // verify that only two changes were processed successfully: assertEquals(2, processedChanges.size()); // verify that successful changes were undone for (final Map.Entry change : processedChanges) { - verify(vppWriter).process(change.getValue(), change.getKey()); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(change.getValue())), + eq(singletonList(change.getKey())), any(WriteContext.class)); } return; } @@ -204,7 +212,8 @@ public class VPPConfigDataTreeTest { // reject third applied change and fourth (first undo): final List> processedChanges = new ArrayList<>(); final Answer answer = new VppWriterAnswer(processedChanges, Arrays.asList(1,2), Arrays.asList(3,4)); - doAnswer(answer).when(vppWriter).process(any(DataObject.class), any(DataObject.class)); + doAnswer(answer).when(vppWriter).update(Matchers.>any(), anyListOf(DataObject.class), + anyListOf(DataObject.class), any(WriteContext.class)); // Prepare modification: final DataTreeCandidateNode rootNode = mockRootNode(); @@ -220,20 +229,25 @@ public class VPPConfigDataTreeTest { proxy.commit(modification); } catch (DataValidationFailedException | VppApiInvocationException e) { // verify that all changes were processed: - verify(vppWriter).process(dataBefore1, dataAfter1); - verify(vppWriter).process(dataBefore2, dataAfter2); - verify(vppWriter).process(dataBefore3, dataAfter3); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(dataBefore1)), + eq(singletonList(dataAfter1)), any(WriteContext.class)); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(dataBefore2)), + eq(singletonList(dataAfter2)), any(WriteContext.class)); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(dataBefore3)), + eq(singletonList(dataAfter3)), any(WriteContext.class)); // verify that only two changes were processed successfully: assertEquals(2, processedChanges.size()); // verify we tried to undo the last successful change: Map.Entry change = processedChanges.get(1); - verify(vppWriter).process(change.getValue(), change.getKey()); + verify(vppWriter).update(Matchers.>any(), eq(singletonList(change.getValue())), + eq(singletonList(change.getKey())), any(WriteContext.class)); // but failed, and did not try to undo the first: change = processedChanges.get(0); - verify(vppWriter, never()).process(change.getValue(), change.getKey()); + verify(vppWriter, never()).update(Matchers.>any(), eq(singletonList(change.getValue())), + eq(singletonList(change.getKey())), any(WriteContext.class)); return; } @@ -295,12 +309,12 @@ public class VPPConfigDataTreeTest { public Object answer(final InvocationOnMock invocation) throws Throwable { ++count; if (toCapture.contains(count)) { - final DataObject dataBefore = (DataObject)invocation.getArguments()[0]; - final DataObject dataAfter = (DataObject)invocation.getArguments()[1]; + final DataObject dataBefore = (DataObject) ((List)invocation.getArguments()[1]).get(0); + final DataObject dataAfter = (DataObject) ((List)invocation.getArguments()[2]).get(0); capturedChanges.add(new AbstractMap.SimpleImmutableEntry<>(dataBefore, dataAfter)); } if (toReject.contains(count)) { - throw mock(VppApiInvocationException.class); + throw mock(RuntimeException.class); } return null; } 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 5ac1f0130..30cebf2f9 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 @@ -44,14 +44,17 @@ public class VppDataBrokerInitializationProviderTest { private WriteTransaction writeTx; @Mock private ReaderRegistry readerRegistry; + @Mock + private VppWriterRegistry writerRegistry; private VppDataBrokerInitializationProvider provider; + @Before public void setUp() throws Exception { initMocks(this); doReturn(writeTx).when(bindingBroker).newWriteOnlyTransaction(); - provider = new VppDataBrokerInitializationProvider(bindingBroker, readerRegistry); + provider = new VppDataBrokerInitializationProvider(bindingBroker, readerRegistry, writerRegistry); } @Test diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationExceptionTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationExceptionTest.java new file mode 100644 index 000000000..fed792a3b --- /dev/null +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationExceptionTest.java @@ -0,0 +1,46 @@ +/* + * 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.trans; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Random; +import org.junit.Test; + +public class VppApiInvocationExceptionTest { + + @Test + public void testInstantiation() { + final String apiMethodName = "methodName"; + final int ctxId = 1; + final int code = -1; + VppApiInvocationException e = new VppApiInvocationException(apiMethodName, ctxId, code); + assertEquals(apiMethodName, e.getMethodName()); + assertEquals(ctxId, e.getCtxId()); + assertEquals(code, e.getErrorCode()); + assertTrue(e.getMessage().contains(apiMethodName)); + assertTrue(e.getMessage().contains(String.valueOf(code))); + assertTrue(e.getMessage().contains(String.valueOf(ctxId))); + } + + @Test(expected = IllegalArgumentException.class) + public void testInstantiationFailed() { + final int code = new Random().nextInt(Integer.MAX_VALUE); + VppApiInvocationException e = new VppApiInvocationException("apiMethodName", 1, code); + } +} diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContextTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContextTest.java new file mode 100644 index 000000000..1a6cf3a5b --- /dev/null +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContextTest.java @@ -0,0 +1,122 @@ +package io.fd.honeycomb.v3po.impl.trans.w.util; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +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.trans.util.Context; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp; +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.BridgeDomains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain; +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.NormalizedNode; + +public class TransactionWriteContextTest { + + @Mock + private BindingNormalizedNodeSerializer serializer; + @Mock + private DOMDataReadOnlyTransaction beforeTx; + @Mock + private DOMDataReadOnlyTransaction afterTx; + @Mock + private CheckedFuture>, ReadFailedException> future; + @Mock + private Optional> optional; + @Mock + private Map.Entry entry; + + private TransactionWriteContext transactionWriteContext; + + @Before + public void setUp() { + initMocks(this); + transactionWriteContext = new TransactionWriteContext(serializer, beforeTx, afterTx); + } + + @Test + public void testReadBeforeNoData() throws Exception { + when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(future); + when(future.checkedGet()).thenReturn(optional); + when(optional.isPresent()).thenReturn(false); + + final InstanceIdentifier instanceId = + InstanceIdentifier.create(Vpp.class).child(BridgeDomains.class).child(BridgeDomain.class); + + final List dataObjects = transactionWriteContext.readBefore(instanceId); + assertNotNull(dataObjects); + assertTrue(dataObjects.isEmpty()); + + verify(serializer).toYangInstanceIdentifier(instanceId); + verify(serializer, never()).fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class)); + } + + + @Test + public void testReadBefore() throws Exception { + when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(future); + when(future.checkedGet()).thenReturn(optional); + when(optional.isPresent()).thenReturn(true); + + final InstanceIdentifier instanceId = + InstanceIdentifier.create(Vpp.class).child(BridgeDomains.class).child(BridgeDomain.class); + final YangInstanceIdentifier yangId = YangInstanceIdentifier.builder().node(VppState.QNAME).node( + BridgeDomains.QNAME).node(BridgeDomain.QNAME).build(); + when(serializer.toYangInstanceIdentifier(any(InstanceIdentifier.class))).thenReturn(yangId); + when(serializer.fromNormalizedNode(eq(yangId), any(NormalizedNode.class))).thenReturn(entry); + + final List dataObjects = transactionWriteContext.readBefore(instanceId); + assertNotNull(dataObjects); + assertFalse(dataObjects.isEmpty()); + + verify(serializer).toYangInstanceIdentifier(instanceId); + verify(serializer).fromNormalizedNode(eq(yangId), any(NormalizedNode.class)); + } + + @Test(expected = IllegalStateException.class) + public void testReadBeforeFailed() throws Exception { + when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(future); + when(future.checkedGet()).thenThrow(ReadFailedException.class); + transactionWriteContext.readBefore(mock(InstanceIdentifier.class)); + } + + @Test(expected = IllegalStateException.class) + public void testReadAfterFailed() throws Exception { + when(afterTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(future); + when(future.checkedGet()).thenThrow(ReadFailedException.class); + transactionWriteContext.readAfter(mock(InstanceIdentifier.class)); + } + + @Test + public void testGetContext() throws Exception { + assertNotNull(transactionWriteContext.getContext()); + } + + @Test + public void testClose() throws Exception { + final Context context = transactionWriteContext.getContext(); + transactionWriteContext.close(); + // TODO verify context was closed + } +} \ No newline at end of file diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans0/VppApiInvocationExceptionTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans0/VppApiInvocationExceptionTest.java deleted file mode 100644 index e83e541d1..000000000 --- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans0/VppApiInvocationExceptionTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.trans0; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Random; -import org.junit.Test; - -public class VppApiInvocationExceptionTest { - - @Test - public void testInstantiation() { - final String apiMethodName = "methodName"; - final int ctxId = 1; - final int code = -1; - VppApiInvocationException e = new VppApiInvocationException(apiMethodName, ctxId, code); - assertEquals(apiMethodName, e.getMethodName()); - assertEquals(ctxId, e.getCtxId()); - assertEquals(code, e.getErrorCode()); - assertTrue(e.getMessage().contains(apiMethodName)); - assertTrue(e.getMessage().contains(String.valueOf(code))); - assertTrue(e.getMessage().contains(String.valueOf(ctxId))); - } - - @Test(expected = IllegalArgumentException.class) - public void testInstantiationFailed() { - final int code = new Random().nextInt(Integer.MAX_VALUE); - VppApiInvocationException e = new VppApiInvocationException("apiMethodName", 1, code); - } -} diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainCustomizerTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainCustomizerTest.java new file mode 100644 index 000000000..4614b875a --- /dev/null +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainCustomizerTest.java @@ -0,0 +1,269 @@ +/* + * 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.vpp; + +import static io.fd.honeycomb.v3po.impl.vpp.BridgeDomainTestUtils.BD_NAME_TO_ID_ANSWER; +import static io.fd.honeycomb.v3po.impl.vpp.BridgeDomainTestUtils.bdIdentifierForName; +import static io.fd.honeycomb.v3po.impl.vpp.BridgeDomainTestUtils.bdNameToID; +import static io.fd.honeycomb.v3po.impl.vpp.BridgeDomainTestUtils.booleanToByte; +import static io.fd.honeycomb.v3po.impl.vpp.BridgeDomainTestUtils.intToBoolean; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.honeycomb.v3po.impl.trans.util.Context; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainKey; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; +import org.openvpp.vppjapi.vppApi; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@SuppressStaticInitializationFor("org.openvpp.vppjapi.vppConn") +@PrepareForTest(vppApi.class) +public class BridgeDomainCustomizerTest { + + private static final int RESPONSE_NOT_READY = -77; + private static final byte ADD_OR_UPDATE_BD = (byte) 1; + private static final byte ZERO = 0; + private vppApi api; + + @Mock + private Context ctx; + + private BridgeDomainCustomizer customizer; + + @Before + public void setUp() throws Exception { + // TODO create base class for tests using vppApi + api = PowerMockito.mock(vppApi.class); + initMocks(this); + customizer = new BridgeDomainCustomizer(api); + + PowerMockito.doAnswer(BD_NAME_TO_ID_ANSWER).when(api).findOrAddBridgeDomainId(anyString()); + PowerMockito.doAnswer(BD_NAME_TO_ID_ANSWER).when(api).bridgeDomainIdFromName(anyString()); + PowerMockito.when(api.getRetval(anyInt(), anyInt())).thenReturn(RESPONSE_NOT_READY).thenReturn(0); + PowerMockito.doReturn(0).when(api).getRetval(anyInt(), anyInt()); + } + + private BridgeDomain generateBridgeDomain(final String bdName) { + final byte arpTerm = 0; + final byte flood = 1; + final byte forward = 0; + final byte learn = 1; + final byte uuf = 0; + return generateBridgeDomain(bdName, arpTerm, flood, forward, learn, uuf); + } + + private BridgeDomain generateBridgeDomain(final String bdName, final int arpTerm, final int flood, + final int forward, final int learn, final int uuf) { + return new BridgeDomainBuilder() + .setName(bdName) + .setArpTermination(intToBoolean(arpTerm)) + .setFlood(intToBoolean(flood)) + .setForward(intToBoolean(forward)) + .setLearn(intToBoolean(learn)) + .setUnknownUnicastFlood(intToBoolean(uuf)) + .build(); + } + + private final int verifyBridgeDomainAddOrUpdateWasInvoked(final BridgeDomain bd) { + final int bdn1Id = bdNameToID(bd.getName()); + final byte arpTerm = booleanToByte(bd.isArpTermination()); + final byte flood = booleanToByte(bd.isFlood()); + final byte forward = booleanToByte(bd.isForward()); + final byte learn = booleanToByte(bd.isLearn()); + final byte uuf = booleanToByte(bd.isUnknownUnicastFlood()); + return verify(api).bridgeDomainAddDel(bdn1Id, flood, forward, learn, uuf, arpTerm, ADD_OR_UPDATE_BD); + } + + private int verifyBridgeDomainAddOrUpdateWasNotInvoked(final BridgeDomain bd) { + final int bdn1Id = bdNameToID(bd.getName()); + final byte arpTerm = booleanToByte(bd.isArpTermination()); + final byte flood = booleanToByte(bd.isFlood()); + final byte forward = booleanToByte(bd.isForward()); + final byte learn = booleanToByte(bd.isLearn()); + final byte uuf = booleanToByte(bd.isUnknownUnicastFlood()); + return verify(api, never()).bridgeDomainAddDel(bdn1Id, flood, forward, learn, uuf, arpTerm, ADD_OR_UPDATE_BD); + } + + private int verifyBridgeDomainDeletedWasInvoked(final BridgeDomain bd) { + final int bdn1Id = bdNameToID(bd.getName()); + return verify(api).bridgeDomainAddDel(bdn1Id, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO); + } + + private int verifyBridgeDomainDeletedWasNotInvoked(final BridgeDomain bd) { + final int bdn1Id = bdNameToID(bd.getName()); + return verify(api, never()).bridgeDomainAddDel(bdn1Id, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO); + } + + @Test + public void testAddBridgeDomain() { + final String bdName = "bd1"; + final BridgeDomain bd = generateBridgeDomain("bd1"); + + customizer.writeCurrentAttributes(bdIdentifierForName(bdName), bd, ctx); + + verifyBridgeDomainAddOrUpdateWasInvoked(bd); + } + + @Test + public void testBridgeDomainNameCreateFailed() { + final String bdName = "bd1"; + final BridgeDomain bd = generateBridgeDomain("bd1"); + + // make vpp api fail to create id for our bd name + PowerMockito.doReturn(-1).when(api).findOrAddBridgeDomainId(bdName); + + try { + customizer.writeCurrentAttributes(bdIdentifierForName(bdName), bd, ctx); + } catch (IllegalStateException e) { + verifyBridgeDomainAddOrUpdateWasNotInvoked(bd); + return; + } + fail("IllegalStateException was expected"); + } + + @Test + public void testAddBridgeDomainFailed() { + // make any call to vpp fail + PowerMockito.doReturn(-1).when(api).getRetval(anyInt(), anyInt()); + + final String bdName = "bd1"; + final BridgeDomain bd = generateBridgeDomain(bdName); + + try { + customizer.writeCurrentAttributes(bdIdentifierForName(bdName), bd, ctx); + } catch (IllegalStateException e) { + verifyBridgeDomainAddOrUpdateWasInvoked(bd); + return; + } + fail("IllegalStateException was expected"); + } + + @Test + public void testDeleteBridgeDomain() { + final String bdName = "bd1"; + final BridgeDomain bd = generateBridgeDomain("bd1"); + + customizer.deleteCurrentAttributes(bdIdentifierForName(bdName), bd, ctx); + + verifyBridgeDomainDeletedWasInvoked(bd); + } + + @Test + public void testDeleteUnknownBridgeDomain() { + final String bdName = "bd1"; + final BridgeDomain bd = generateBridgeDomain("bd1"); + + // make vpp api not find our bd + PowerMockito.doReturn(-1).when(api).bridgeDomainIdFromName(bdName); + + try { + customizer.deleteCurrentAttributes(bdIdentifierForName(bdName), bd, ctx); + } catch (IllegalStateException e) { + verifyBridgeDomainDeletedWasNotInvoked(bd); + return; + } + fail("IllegalStateException was expected"); + } + + @Test + public void testDeleteBridgeDomainFailed() { + // make any call to vpp fail + PowerMockito.doReturn(-1).when(api).getRetval(anyInt(), anyInt()); + + final String bdName = "bd1"; + final BridgeDomain bd = generateBridgeDomain(bdName); + + try { + customizer.deleteCurrentAttributes(bdIdentifierForName(bdName), bd, ctx); + } catch (IllegalStateException e) { + verifyBridgeDomainDeletedWasInvoked(bd); + return; + } + fail("IllegalStateException was expected"); + } + + @Test + public void testUpdateBridgeDomain() throws Exception { + final String bdName = "bd1"; + final byte arpTermBefore = 1; + final byte floodBefore = 1; + final byte forwardBefore = 0; + final byte learnBefore = 1; + final byte uufBefore = 0; + + final BridgeDomain dataBefore = + generateBridgeDomain(bdName, arpTermBefore, floodBefore, forwardBefore, learnBefore, uufBefore); + final BridgeDomain dataAfter = + generateBridgeDomain(bdName, arpTermBefore ^ 1, floodBefore ^ 1, forwardBefore ^ 1, learnBefore ^ 1, + uufBefore ^ 1); + + final KeyedInstanceIdentifier id = bdIdentifierForName(bdName); + + customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx); + + verifyBridgeDomainAddOrUpdateWasInvoked(dataAfter); + } + + @Test + public void testUpdateUnknownBridgeDomain() throws Exception { + final String bdName = "bd1"; + final BridgeDomain bd = generateBridgeDomain("bd1"); + + // make vpp api not find our bd + PowerMockito.doReturn(-1).when(api).bridgeDomainIdFromName(bdName); + + try { + customizer.updateCurrentAttributes(bdIdentifierForName(bdName), bd, bd, ctx); + } catch (IllegalStateException e) { + verifyBridgeDomainAddOrUpdateWasNotInvoked(bd); + return; + } + fail("IllegalStateException was expected"); + } + + @Test + public void testUpdateBridgeDomainFailed() { + // make any call to vpp fail + PowerMockito.doReturn(-1).when(api).getRetval(anyInt(), anyInt()); + + final String bdName = "bd1"; + final BridgeDomain bd = generateBridgeDomain(bdName); + + try { + customizer.updateCurrentAttributes(bdIdentifierForName(bdName), bd, bd, ctx); + } catch (IllegalStateException e) { + verifyBridgeDomainAddOrUpdateWasInvoked(bd); + return; + } + fail("IllegalStateException was expected"); + } + +} \ No newline at end of file diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainTestUtils.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainTestUtils.java new file mode 100644 index 000000000..6930493b6 --- /dev/null +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/BridgeDomainTestUtils.java @@ -0,0 +1,64 @@ +/* + * 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.vpp; + +import javax.annotation.Nullable; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +final class BridgeDomainTestUtils { + + private BridgeDomainTestUtils() { + throw new UnsupportedOperationException("Utility class cannot be instantiated."); + } + + public static byte booleanToByte(@Nullable final Boolean value) { + return value != null && value ? (byte) 1 : (byte) 0; + } + + @Nullable + public static Boolean intToBoolean(final int value) { + if (value == 0) { + return Boolean.FALSE; + } + if (value == 1) { + return Boolean.TRUE; + } + return null; + } + + public static int bdNameToID(String bName) { + return Integer.parseInt(((Character)bName.charAt(bName.length() - 1)).toString()); + } + + public static KeyedInstanceIdentifier bdIdentifierForName( + final String bdName) { + return InstanceIdentifier.create(BridgeDomains.class).child(BridgeDomain.class, new BridgeDomainKey(bdName)); + } + + public static final Answer BD_NAME_TO_ID_ANSWER = new Answer() { + @Override + public Integer answer(final InvocationOnMock invocationOnMock) throws Throwable { + return bdNameToID((String) invocationOnMock.getArguments()[0]); + } + }; +} \ No newline at end of file diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/VppTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/VppTest.java index beec76ab1..bf7b9de4b 100644 --- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/VppTest.java +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vpp/VppTest.java @@ -16,6 +16,7 @@ package io.fd.honeycomb.v3po.impl.vpp; +import static io.fd.honeycomb.v3po.impl.vpp.BridgeDomainTestUtils.BD_NAME_TO_ID_ANSWER; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@ -33,8 +34,6 @@ import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains; @@ -71,14 +70,8 @@ public class VppTest { public void setUp() throws Exception { api = PowerMockito.mock(vppApi.class); ctx = mock(WriteContext.class); - PowerMockito.doAnswer(new Answer() { - @Override - public Integer answer(final InvocationOnMock invocationOnMock) throws Throwable { - final String bName = (String) invocationOnMock.getArguments()[0]; - return Integer.parseInt(((Character)bName.charAt(bName.length() - 1)).toString()); - } - }).when(api).findOrAddBridgeDomainId(anyString()); - PowerMockito.doReturn(1).when(api).bridgeDomainIdFromName(anyString()); + PowerMockito.doAnswer(BD_NAME_TO_ID_ANSWER).when(api).findOrAddBridgeDomainId(anyString()); + PowerMockito.doAnswer(BD_NAME_TO_ID_ANSWER).when(api).bridgeDomainIdFromName(anyString()); PowerMockito.doReturn(1).when(api).getRetval(anyInt(), anyInt()); vppWriter = VppUtils.getVppWriter(api); rootRegistry = new DelegatingWriterRegistry( -- cgit 1.2.3-korg