diff options
20 files changed, 750 insertions, 276 deletions
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<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( + @Nonnull final YangInstanceIdentifier path) { + return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>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<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore(); final Optional<NormalizedNode<?, ?>> 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<InstanceIdentifier<?>, DataObject> nodesBefore = extractNetconfData(normalizedDataBefore); - LOG.debug("VppConfigDataProxy.commit() extracted nodesBefore={}", nodesBefore.keySet()); + LOG.debug("VppConfigDataTree.commit() extracted nodesBefore={}", nodesBefore.keySet()); final Map<InstanceIdentifier<?>, 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<InstanceIdentifier<?>, DataObject> extractNetconfData(final Optional<NormalizedNode<?, ?>> parentOptional) { + private Map<InstanceIdentifier<?>, DataObject> extractNetconfData( + final Optional<NormalizedNode<?, ?>> 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<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( - final YangInstanceIdentifier path) { + @Nonnull final YangInstanceIdentifier path) { final Optional<NormalizedNode<?, ?>> 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<InstanceIdentifier<?>> processedNodes; private final Map<InstanceIdentifier<?>, DataObject> nodesBefore; private final Map<InstanceIdentifier<?>, DataObject> nodesAfter; + private final WriteContext ctx; - ChangesProcessor(@Nonnull final VppWriter writer, + ChangesProcessor(@Nonnull final VppWriterRegistry writer, final Map<InstanceIdentifier<?>, DataObject> nodesBefore, - final Map<InstanceIdentifier<?>, 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<InstanceIdentifier<?>, 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<InstanceIdentifier<?>> 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<DataObject> dataObjectsBefore = dataBefore == null + ? Collections.<DataObject>emptyList() + : singletonList(dataBefore); + final List<DataObject> dataObjectsAfter = dataAfter == null + ? Collections.<DataObject>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<InstanceIdentifier<?>> 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<Node> mountPointPath; + private final VppWriterRegistry writerRegistry; private ObjectRegistration<DOMMountPoint> 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<DataObject> { + + private static VppWriterRegistry instance; + + private final DelegatingWriterRegistry writer; + + private VppWriterRegistry(@Nonnull final vppApi vppApi) { + final CompositeRootVppWriter<Vpp> vppWriter = initVppStateWriter(vppApi); + writer = new DelegatingWriterRegistry(Collections.<VppWriter<? extends DataObject>>singletonList(vppWriter)); + } + + private static CompositeRootVppWriter<Vpp> initVppStateWriter(@Nonnull final vppApi vppApi) { + final CompositeListVppWriter<BridgeDomain, BridgeDomainKey> bridgeDomainWriter = new CompositeListVppWriter<>( + BridgeDomain.class, + new io.fd.honeycomb.v3po.impl.vpp.BridgeDomainCustomizer(vppApi)); + + final ChildVppWriter<BridgeDomains> bridgeDomainsWriter = new CompositeChildVppWriter<>( + BridgeDomains.class, + VppRWUtils.singletonChildWriterList(bridgeDomainWriter), + new ReflexiveChildWriterCustomizer<BridgeDomains>()); + + final List<ChildVppWriter<? extends ChildOf<Vpp>>> childWriters = new ArrayList<>(); + childWriters.add(bridgeDomainsWriter); + + return new CompositeRootVppWriter<>( + Vpp.class, + childWriters, + new NoopWriterCustomizer<Vpp>()); + } + + public static synchronized VppWriterRegistry getInstance(@Nonnull final vppApi vppApi) { + if (instance == null) { + instance = new VppWriterRegistry(vppApi); + } + return instance; + } + + @Nonnull + @Override + public InstanceIdentifier<DataObject> getManagedDataObjectType() { + return writer.getManagedDataObjectType(); + } + + @Override + public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id, + @Nonnull final List<? extends DataObject> dataBefore, + @Nonnull final List<? extends DataObject> data, @Nonnull final WriteContext ctx) { + writer.update(id, dataBefore, data, ctx); + } +} 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/trans/VppApiInvocationException.java index 119563bdd..b0076cd9c 100644 --- 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/trans/VppApiInvocationException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.fd.honeycomb.v3po.impl.trans0; +package io.fd.honeycomb.v3po.impl.trans; import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; 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<? extends DataObject> read(final InstanceIdentifier<? extends DataObject> currentId, - final ReadOnlyTransaction tx) { + final DOMDataReadOnlyTransaction tx) { // FIXME how to read all for list (using wildcarded ID) ? - final CheckedFuture<? extends Optional<? extends DataObject>, ReadFailedException> read = - tx.read(LogicalDatastoreType.CONFIGURATION, currentId); + final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(currentId); + + final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read = + tx.read(LogicalDatastoreType.CONFIGURATION, path); + try { - final Optional<? extends DataObject> optional = read.checkedGet(); - return optional.isPresent() - ? Collections.singletonList(optional.get()) - : Collections.<DataObject>emptyList(); + final Optional<NormalizedNode<?, ?>> optional = read.checkedGet(); + + if (!optional.isPresent()) { + return Collections.<DataObject>emptyList(); + } + + final NormalizedNode<?, ?> data = optional.get(); + final Map.Entry<InstanceIdentifier<?>, 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/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<Interfaces> { - private static final Logger LOG = LoggerFactory.getLogger(VppInterfacesReader.class); - - @Override - public Interfaces read(final InstanceIdentifier<? extends DataObject> 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<Interfaces> 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 extends DataObject> { - - C read(InstanceIdentifier<? extends DataObject> id); - - Class<C> 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<C extends DataObject> { - - 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<BridgeDomain> 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<BridgeDomain> 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<BridgeDomain> 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<DataObject> 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.<InstanceIdentifier<?>>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<Map.Entry<DataObject, DataObject>> 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.<InstanceIdentifier<?>>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.<InstanceIdentifier<?>>any(), eq(singletonList(dataBefore1)), + eq(singletonList(dataAfter1)), any(WriteContext.class)); + verify(vppWriter).update(Matchers.<InstanceIdentifier<?>>any(), eq(singletonList(dataBefore2)), + eq(singletonList(dataAfter2)), any(WriteContext.class)); + verify(vppWriter).update(Matchers.<InstanceIdentifier<?>>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<DataObject, DataObject> change : processedChanges) { - verify(vppWriter).process(change.getValue(), change.getKey()); + verify(vppWriter).update(Matchers.<InstanceIdentifier<?>>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<Map.Entry<DataObject, DataObject>> 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.<InstanceIdentifier<?>>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.<InstanceIdentifier<?>>any(), eq(singletonList(dataBefore1)), + eq(singletonList(dataAfter1)), any(WriteContext.class)); + verify(vppWriter).update(Matchers.<InstanceIdentifier<?>>any(), eq(singletonList(dataBefore2)), + eq(singletonList(dataAfter2)), any(WriteContext.class)); + verify(vppWriter).update(Matchers.<InstanceIdentifier<?>>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<DataObject, DataObject> change = processedChanges.get(1); - verify(vppWriter).process(change.getValue(), change.getKey()); + verify(vppWriter).update(Matchers.<InstanceIdentifier<?>>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.<InstanceIdentifier<?>>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/trans0/VppApiInvocationExceptionTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/trans/VppApiInvocationExceptionTest.java index e83e541d1..fed792a3b 100644 --- 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/trans/VppApiInvocationExceptionTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.fd.honeycomb.v3po.impl.trans0; +package io.fd.honeycomb.v3po.impl.trans; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; 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<Optional<NormalizedNode<?, ?>>, ReadFailedException> future; + @Mock + private Optional<org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode<?, ?>> 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<BridgeDomain> instanceId = + InstanceIdentifier.create(Vpp.class).child(BridgeDomains.class).child(BridgeDomain.class); + + final List<? extends DataObject> 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<BridgeDomain> 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<? extends DataObject> 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/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<BridgeDomain, BridgeDomainKey> 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<BridgeDomain, BridgeDomainKey> bdIdentifierForName( + final String bdName) { + return InstanceIdentifier.create(BridgeDomains.class).child(BridgeDomain.class, new BridgeDomainKey(bdName)); + } + + public static final Answer<Integer> BD_NAME_TO_ID_ANSWER = new Answer<Integer>() { + @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<Integer>() { - @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( |