diff options
Diffstat (limited to 'v3po')
87 files changed, 1741 insertions, 1012 deletions
diff --git a/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java b/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java new file mode 100644 index 000000000..d05c55716 --- /dev/null +++ b/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java @@ -0,0 +1,70 @@ +/* + * 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.data; + +import com.google.common.annotations.Beta; +import io.fd.honeycomb.v3po.translate.TranslationException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; + +/** + * Modification of a {@link ModifiableDataManager}. + */ +@Beta +public interface DataModification extends ReadableDataManager { + + /** + * Delete the node at specified path. + * + * @param path Node path + */ + void delete(YangInstanceIdentifier path); + + /** + * Merge the specified data with the currently-present data + * at specified path. + * + * @param path Node path + * @param data Data to be merged + */ + void merge(YangInstanceIdentifier path, NormalizedNode<?, ?> data); + + /** + * Replace the data at specified path with supplied data. + * + * @param path Node path + * @param data New node data + */ + void write(YangInstanceIdentifier path, NormalizedNode<?, ?> data); + + /** + * Alters data tree using this modification. + * + * @throws DataValidationFailedException if modification data is not valid + * @throws TranslationException if failed while updating data tree state + */ + void commit() throws DataValidationFailedException, TranslationException; + + /** + * Validate and prepare modification before commit. Besides commit, no further operation is expected after validate + * and the behaviour is undefined. + * + * @throws DataValidationFailedException if modification data is not valid + */ + void validate() throws DataValidationFailedException; +} diff --git a/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataTreeSnapshot.java b/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java index cae459299..11cd513ea 100644 --- a/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataTreeSnapshot.java +++ b/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java @@ -17,18 +17,17 @@ package io.fd.honeycomb.v3po.data; import com.google.common.annotations.Beta; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; /** - * Read-only snapshot of a {@link ReadableDataTree}. + * Facade over data tree that allows tree modification. */ @Beta -public interface DataTreeSnapshot extends ReadableDataTree { +public interface ModifiableDataManager extends ReadableDataManager { /** - * Creates a new data tree modification. + * Creates read-only snapshot of a ModifiableDataTree. * - * @return A new data tree modification + * @return modification */ - DataTreeModification newModification(); + DataModification newModification(); } diff --git a/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataTree.java b/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataTree.java deleted file mode 100644 index 8b21ddf24..000000000 --- a/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataTree.java +++ /dev/null @@ -1,44 +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.data; - -import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.TranslationException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; - -/** - * Facade over data tree that allows tree modification. - */ -@Beta -public interface ModifiableDataTree { - /** - * Alters data tree using supplied modification. - * - * @param modification data tree modification - * @throws DataValidationFailedException if modification data is not valid - * @throws TranslationException if failed while updating data tree state - */ - void modify(final DataTreeModification modification) throws DataValidationFailedException, TranslationException; - - /** - * Creates read-only snapshot of a ModifiableDataTree. - * - * @return Data tree snapshot. - */ - DataTreeSnapshot takeSnapshot(); -} diff --git a/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataTree.java b/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java index 1cb8f806d..0e98c0903 100644 --- a/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataTree.java +++ b/v3po/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java @@ -28,7 +28,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; * Facade over data tree that allows reading tree nodes. */ @Beta -public interface ReadableDataTree { +public interface ReadableDataManager { + /** * Reads a particular node from the data tree. * diff --git a/v3po/data-api/src/main/yang/data-api.yang b/v3po/data-api/src/main/yang/data-api.yang index 92def4bc4..11d963e4b 100644 --- a/v3po/data-api/src/main/yang/data-api.yang +++ b/v3po/data-api/src/main/yang/data-api.yang @@ -21,12 +21,12 @@ module data-api { identity honeycomb-readable-data-tree { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.data.ReadableDataTree; + config:java-class io.fd.honeycomb.v3po.data.ReadableDataManager; } identity honeycomb-modifiable-data-tree { base "config:service-type"; - config:java-class io.fd.honeycomb.v3po.data.ModifiableDataTree; + config:java-class io.fd.honeycomb.v3po.data.ModifiableDataManager; } }
\ No newline at end of file diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java deleted file mode 100644 index 1e118ff2a..000000000 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java +++ /dev/null @@ -1,173 +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.data.impl; - -import static com.google.common.base.Preconditions.checkNotNull; -import static io.fd.honeycomb.v3po.data.impl.DataTreeUtils.childrenFromNormalized; - -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.translate.TranslationException; -import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriterRegistry; -import java.util.Collections; -import java.util.Map; -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.yangtools.binding.data.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.DataContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * DataTree implementation for configuration data. - */ -public final class ConfigDataTree implements ModifiableDataTree { - - private static final Logger LOG = LoggerFactory.getLogger(ConfigDataTree.class); - - private final BindingNormalizedNodeSerializer serializer; - private final DataTree dataTree; - private final WriterRegistry writerRegistry; - public static final ReadableDataTree EMPTY_OPERATIONAL = new ReadableDataTree() { - @Override - public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( - @Nonnull final YangInstanceIdentifier path) { - return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>absent()); - } - }; - - /** - * Creates configuration data tree instance. - * - * @param serializer service for serialization between Java Binding Data representation and NormalizedNode - * representation. - * @param dataTree data tree for configuration data representation - * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing - * bulk updates. - */ - public ConfigDataTree(@Nonnull final BindingNormalizedNodeSerializer serializer, - @Nonnull final DataTree dataTree, @Nonnull final WriterRegistry writerRegistry) { - this.serializer = checkNotNull(serializer, "serializer should not be null"); - this.dataTree = checkNotNull(dataTree, "dataTree should not be null"); - this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null"); - } - - @Override - public DataTreeSnapshot takeSnapshot() { - return new ConfigSnapshot(dataTree.takeSnapshot()); - } - - @Override - public void modify(final DataTreeModification modification) - throws DataValidationFailedException, TranslationException { - LOG.debug("ConfigDataTree.modify"); - - dataTree.validate(modification); - - final DataTreeCandidate candidate = dataTree.prepare(modification); - - final DataTreeCandidateNode rootNode = candidate.getRootNode(); - final YangInstanceIdentifier rootPath = candidate.getRootPath(); - final Optional<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore(); - final Optional<NormalizedNode<?, ?>> normalizedDataAfter = rootNode.getDataAfter(); - LOG.debug("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", - rootPath, rootNode, normalizedDataBefore, normalizedDataAfter); - - final Map<InstanceIdentifier<?>, DataObject> nodesBefore = extractNetconfData(normalizedDataBefore); - LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet()); - - final Map<InstanceIdentifier<?>, DataObject> nodesAfter = extractNetconfData(normalizedDataAfter); - LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet()); - - final DOMDataReadOnlyTransaction beforeTx = new ReadOnlyTransaction(EMPTY_OPERATIONAL, takeSnapshot()); - final ConfigSnapshot modificationSnapshot = new ConfigSnapshot(modification); - final DOMDataReadOnlyTransaction afterTx = new ReadOnlyTransaction(EMPTY_OPERATIONAL, modificationSnapshot); - try (final WriteContext ctx = new TransactionWriteContext(serializer, beforeTx, afterTx)) { - writerRegistry.update(nodesBefore, nodesAfter, ctx); - } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) { - LOG.warn("Failed to apply all changes", e); - LOG.info("Trying to revert successful changes for current transaction"); - - try { - e.revertChanges(); - LOG.info("Changes successfully reverted"); - } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException) { - // fail with failed revert - LOG.error("Failed to revert successful changes", revertFailedException); - throw revertFailedException; - } - - throw e; // fail with success revert - } catch (TranslationException e) { - LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e); - throw e; - } - - dataTree.commit(candidate); - } - - private Map<InstanceIdentifier<?>, DataObject> extractNetconfData( - final Optional<NormalizedNode<?, ?>> parentOptional) { - if (parentOptional.isPresent()) { - final DataContainerNode parent = (DataContainerNode) parentOptional.get(); - return childrenFromNormalized(parent, serializer); - } - return Collections.emptyMap(); - } - - private final static class ConfigSnapshot implements DataTreeSnapshot { - private final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot; - - ConfigSnapshot(@Nonnull final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot) { - this.snapshot = snapshot; - } - - @Override - public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( - @Nonnull final YangInstanceIdentifier path) { - final Optional<NormalizedNode<?, ?>> node = snapshot.readNode(path); - if (LOG.isTraceEnabled() && node.isPresent()) { - LOG.trace("ConfigSnapshot.read: {}", node.get()); - } - return Futures.immediateCheckedFuture(node); - } - - @Override - public DataTreeModification newModification() { - return snapshot.newModification(); - } - } -} - - - diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java index a0b585143..c418ed332 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java @@ -16,10 +16,13 @@ package io.fd.honeycomb.v3po.data.impl; -import com.google.common.base.Preconditions; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.ReadableDataTree; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import java.io.Closeable; +import java.io.IOException; import java.util.Collections; import java.util.Map; import javax.annotation.Nonnull; @@ -42,45 +45,36 @@ import org.slf4j.LoggerFactory; * Data Broker which provides data transaction functionality for YANG capable data provider using {@link NormalizedNode} * data format. */ -public class DataBroker implements DOMDataBroker { +public class DataBroker implements DOMDataBroker, Closeable { private static final Logger LOG = LoggerFactory.getLogger(DataBroker.class); - private final ReadableDataTree operationalDataTree; - private final ModifiableDataTree configDataTree; + private final TransactionFactory transactionFactory; /** * Creates DataBroker instance. * - * @param operationalDataTree operational data - * @param configDataTree configuration data + * @param transactionFactory transaction producing factory */ - public DataBroker(@Nonnull final ReadableDataTree operationalDataTree, - @Nonnull final ModifiableDataTree configDataTree) { - this.operationalDataTree = - Preconditions.checkNotNull(operationalDataTree, "operationalDataTree should not be null"); - this.configDataTree = Preconditions.checkNotNull(configDataTree, "configDataTree should not be null"); - LOG.trace("DataBroker({}).init() operationalDataTree={}, configDataTree={}", this, operationalDataTree, configDataTree); + public DataBroker(final TransactionFactory transactionFactory) { + this.transactionFactory = transactionFactory; } @Override public DOMDataReadOnlyTransaction newReadOnlyTransaction() { LOG.trace("DataBroker({}).newReadOnlyTransaction()", this); - return new ReadOnlyTransaction(operationalDataTree, configDataTree.takeSnapshot()); + return transactionFactory.newReadOnlyTransaction(); } @Override public DOMDataReadWriteTransaction newReadWriteTransaction() { LOG.trace("DataBroker({}).newReadWriteTransaction()", this); - final DataTreeSnapshot configSnapshot = configDataTree.takeSnapshot(); - final DOMDataReadOnlyTransaction readOnlyTx = new ReadOnlyTransaction(operationalDataTree, configSnapshot); - final DOMDataWriteTransaction writeOnlyTx = new WriteTransaction(configDataTree, configSnapshot); - return new ReadWriteTransaction(readOnlyTx, writeOnlyTx); + return transactionFactory.newReadWriteTransaction(); } @Override public DOMDataWriteTransaction newWriteOnlyTransaction() { LOG.trace("DataBroker({}).newWriteOnlyTransaction()", this); - return new WriteTransaction(configDataTree); + return transactionFactory.newWriteOnlyTransaction(); } @Override @@ -101,6 +95,104 @@ public class DataBroker implements DOMDataBroker { public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() { return Collections.emptyMap(); } + + /** + * Create DataBroker for a modifiable config DT, but only readable Operational + */ + @Nonnull + public static DataBroker create(@Nonnull final ModifiableDataManager configDataTree, + @Nonnull final ReadableDataManager operationalDataTree) { + checkNotNull(operationalDataTree, "operationalDataTree should not be null"); + checkNotNull(configDataTree, "configDataTree should not be null"); + return new DataBroker(new MainPipelineTxFactory(configDataTree, operationalDataTree)); + } + + /** + * Create DataBroker for modifiable operational DT, but no support for config + */ + @Nonnull + public static DataBroker create(@Nonnull final ModifiableDataManager operationalDataTree) { + checkNotNull(operationalDataTree, "operationalDataTree should not be null"); + return new DataBroker(new ContextPipelineTxFactory(operationalDataTree)); + } + + @Override + public void close() throws IOException { + // NOOP + } + + /** + * Transaction provider factory to be used by {@link DataBroker} + */ + public interface TransactionFactory { + + DOMDataReadOnlyTransaction newReadOnlyTransaction(); + + DOMDataReadWriteTransaction newReadWriteTransaction(); + + DOMDataWriteTransaction newWriteOnlyTransaction(); + } + + /** + * Transaction factory specific for Honeycomb's main pipeline (config: read+write, operational: read-only) + */ + private static class MainPipelineTxFactory implements TransactionFactory { + private final ReadableDataManager operationalDataTree; + private final ModifiableDataManager configDataTree; + + MainPipelineTxFactory(@Nonnull final ModifiableDataManager configDataTree, + @Nonnull final ReadableDataManager operationalDataTree) { + this.operationalDataTree = operationalDataTree; + this.configDataTree = configDataTree; + } + + @Override + public DOMDataReadOnlyTransaction newReadOnlyTransaction() { + return ReadOnlyTransaction.create(configDataTree.newModification(), operationalDataTree); + } + + @Override + public DOMDataReadWriteTransaction newReadWriteTransaction() { + final DataModification configModification = configDataTree.newModification(); + return new ReadWriteTransaction( + ReadOnlyTransaction.create(configModification, operationalDataTree), + WriteTransaction.createConfigOnly(configModification)); + } + + @Override + public DOMDataWriteTransaction newWriteOnlyTransaction() { + return WriteTransaction.createConfigOnly(configDataTree.newModification()); + } + } + + /** + * Transaction factory specific for Honeycomb's context pipeline (config: none, operational: read+write) + */ + private static class ContextPipelineTxFactory implements TransactionFactory { + private final ModifiableDataManager operationalDataTree; + + ContextPipelineTxFactory(@Nonnull final ModifiableDataManager operationalDataTree) { + this.operationalDataTree = operationalDataTree; + } + + @Override + public DOMDataReadOnlyTransaction newReadOnlyTransaction() { + return ReadOnlyTransaction.createOperationalOnly(operationalDataTree); + } + + @Override + public DOMDataReadWriteTransaction newReadWriteTransaction() { + final DataModification dataModification = operationalDataTree.newModification(); + return new ReadWriteTransaction( + ReadOnlyTransaction.createOperationalOnly(dataModification), + WriteTransaction.createOperationalOnly(dataModification)); + } + + @Override + public DOMDataWriteTransaction newWriteOnlyTransaction() { + return WriteTransaction.createOperationalOnly(operationalDataTree.newModification()); + } + } } diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java index de83a198b..5833459ea 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java @@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory; * Utility class for various operations on DataTree. */ final class DataTreeUtils { + private static final Logger LOG = LoggerFactory.getLogger(DataTreeUtils.class); private DataTreeUtils() { diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java new file mode 100644 index 000000000..2b9010747 --- /dev/null +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java @@ -0,0 +1,174 @@ +/* + * 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.data.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; +import static io.fd.honeycomb.v3po.data.impl.DataTreeUtils.childrenFromNormalized; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.translate.TranslationException; +import io.fd.honeycomb.v3po.translate.util.write.TransactionMappingContext; +import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext; +import io.fd.honeycomb.v3po.translate.write.WriteContext; +import io.fd.honeycomb.v3po.translate.write.WriterRegistry; +import java.util.Collections; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.yangtools.binding.data.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.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Extension of {@link ModifiableDataTreeManager} that propagates data changes to underlying writer layer before they + * are fully committed in the backing data tree. Data changes are propagated in BA format. + */ +public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager { + + private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeDelegator.class); + private static final ReadableDataManager EMPTY_OPERATIONAL = p -> immediateCheckedFuture(Optional.absent()); + + // TODO what to use instead of deprecated BindingNormalizedNodeSerializer ? + private final BindingNormalizedNodeSerializer serializer; + private final WriterRegistry writerRegistry; + private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; + + /** + * Creates configuration data tree instance. + * @param serializer service for serialization between Java Binding Data representation and NormalizedNode + * representation. + * @param dataTree data tree for configuration data representation + * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing + * @param contextBroker BA broker providing full access to mapping context data + */ + public ModifiableDataTreeDelegator(@Nonnull final BindingNormalizedNodeSerializer serializer, + @Nonnull final DataTree dataTree, + @Nonnull final WriterRegistry writerRegistry, + @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) { + super(dataTree); + this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null"); + this.serializer = checkNotNull(serializer, "serializer should not be null"); + this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null"); + } + + @Override + public DataModification newModification() { + return new ConfigSnapshot(super.newModification()); + } + + private final class ConfigSnapshot extends ModifiableDataTreeManager.ConfigSnapshot { + + private final DataModification untouchedModification; + + /** + * @param untouchedModification DataModification captured while this modification/snapshot was created. + * To be used later while invoking writers to provide them with before state + * (state without current modifications). + * It must be captured as close as possible to when current modification started. + */ + ConfigSnapshot(final DataModification untouchedModification) { + this.untouchedModification = untouchedModification; + } + + /** + * Pass the changes to underlying writer layer. + * Transform from BI to BA. + * Revert(Write data before to subtrees that have been successfully modified before failure) in case of failure. + */ + @Override + protected void processCandidate(final DataTreeCandidate candidate) + throws TranslationException { + final DataTreeCandidateNode rootNode = candidate.getRootNode(); + final YangInstanceIdentifier rootPath = candidate.getRootPath(); + final Optional<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore(); + final Optional<NormalizedNode<?, ?>> normalizedDataAfter = rootNode.getDataAfter(); + LOG.debug("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", + rootPath, rootNode, normalizedDataBefore, normalizedDataAfter); + + final Map<InstanceIdentifier<?>, DataObject> nodesBefore = toBindingAware(normalizedDataBefore); + LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet()); + final Map<InstanceIdentifier<?>, DataObject> nodesAfter = toBindingAware(normalizedDataAfter); + LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet()); + + try (final WriteContext ctx = getTransactionWriteContext()) { + writerRegistry.update(nodesBefore, nodesAfter, ctx); + + final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult = + ((TransactionMappingContext) ctx.getMappingContext()).submit(); + // Blocking on context data update + contextUpdateResult.checkedGet(); + + } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) { + LOG.warn("Failed to apply all changes", e); + LOG.info("Trying to revert successful changes for current transaction"); + + try { + e.revertChanges(); + LOG.info("Changes successfully reverted"); + } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException) { + // fail with failed revert + LOG.error("Failed to revert successful changes", revertFailedException); + throw revertFailedException; + } + + throw e; // fail with success revert + } catch (TransactionCommitFailedException e) { + // FIXME revert should probably occur when context is not written successfully, but can that even happen ? + final String msg = "Error while updating mapping context data"; + LOG.error(msg, e); + throw new TranslationException(msg, e); + } catch (TranslationException e) { + LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e); + throw e; + } + } + + private TransactionWriteContext getTransactionWriteContext() { + // Before Tx must use modification + final DOMDataReadOnlyTransaction beforeTx = ReadOnlyTransaction.create(untouchedModification, EMPTY_OPERATIONAL); + // After Tx must use current modification + final DOMDataReadOnlyTransaction afterTx = ReadOnlyTransaction.create(this, EMPTY_OPERATIONAL); + final TransactionMappingContext mappingContext = new TransactionMappingContext( + contextBroker.newReadWriteTransaction()); + return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext); + } + + private Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Optional<NormalizedNode<?, ?>> parentOptional) { + if (parentOptional.isPresent()) { + final DataContainerNode parent = (DataContainerNode) parentOptional.get(); + return childrenFromNormalized(parent, serializer); + } + return Collections.emptyMap(); + } + } +} + + + diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java new file mode 100644 index 000000000..1082c479b --- /dev/null +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java @@ -0,0 +1,122 @@ +/* + * 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.data.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.translate.TranslationException; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * DataTree backed implementation for modifiable data manager. + */ +public class ModifiableDataTreeManager implements ModifiableDataManager { + + private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeManager.class); + + private final DataTree dataTree; + + public ModifiableDataTreeManager(@Nonnull final DataTree dataTree) { + this.dataTree = checkNotNull(dataTree, "dataTree should not be null"); + } + + @Override + public DataModification newModification() { + return new ConfigSnapshot(); + } + + @Override + public final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path) { + return newModification().read(path); + } + + protected class ConfigSnapshot implements DataModification { + private final DataTreeModification modification; + private boolean validated = false; + + ConfigSnapshot() { + this(dataTree.takeSnapshot().newModification()); + } + + protected ConfigSnapshot(final DataTreeModification modification) { + this.modification = modification; + } + + @Override + public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( + @Nonnull final YangInstanceIdentifier path) { + final Optional<NormalizedNode<?, ?>> node = modification.readNode(path); + if (LOG.isTraceEnabled() && node.isPresent()) { + LOG.trace("ConfigSnapshot.read: {}", node.get()); + } + return immediateCheckedFuture(node); + } + + @Override + public final void delete(final YangInstanceIdentifier path) { + modification.delete(path); + } + + @Override + public final void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) { + modification.merge(path, data); + } + + @Override + public final void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) { + modification.write(path, data); + } + + @Override + public final void commit() throws DataValidationFailedException, TranslationException { + if(!validated) { + validate(); + } + final DataTreeCandidate candidate = dataTree.prepare(modification); + processCandidate(candidate); + dataTree.commit(candidate); + } + + protected void processCandidate(final DataTreeCandidate candidate) throws TranslationException { + // NOOP + } + + @Override + public final void validate() throws DataValidationFailedException { + modification.ready(); + dataTree.validate(modification); + validated = true; + } + } +} + + + diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java index 25d1d8dda..a54b7f148 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java @@ -127,6 +127,10 @@ public class PersistingDataTreeAdapter implements org.opendaylight.yangtools.yan if(currentRoot.isPresent()) { try { LOG.trace("Persisting current data: {} into: {}", currentRoot.get(), path); + // Make sure the file gets overwritten + if(Files.exists(path)) { + Files.delete(path); + } // TODO once we are in static environment, do the writer, streamWriter and NNWriter initialization only once final JsonWriter jsonWriter = createJsonWriter(Files.newOutputStream(path, StandardOpenOption.CREATE), true); diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java index 83c9e30bd..c38fa27ca 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java @@ -16,14 +16,16 @@ package io.fd.honeycomb.v3po.data.impl; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + import com.google.common.base.Function; 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 com.google.common.util.concurrent.ListenableFuture; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.ReadableDataManager; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -37,17 +39,27 @@ import org.slf4j.LoggerFactory; final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTransaction.class); - private volatile ReadableDataTree operationalData; - private volatile DataTreeSnapshot configSnapshot; - ReadOnlyTransaction(@Nonnull final ReadableDataTree operationalData, - @Nonnull final DataTreeSnapshot configSnapshot) { - this.operationalData = Preconditions.checkNotNull(operationalData, "operationalData should not be null"); - this.configSnapshot = Preconditions.checkNotNull(configSnapshot, "config should not be null"); + @Nullable + private volatile ReadableDataManager operationalData; + @Nullable + private volatile ReadableDataManager configSnapshot; + + private volatile boolean closed = false; + + /** + * @param configData config data tree manager. Null if config reads are not to be supported + * @param operationalData operational data tree manager. Null if operational reads are not to be supported + */ + private ReadOnlyTransaction(@Nullable final ReadableDataManager configData, + @Nullable final ReadableDataManager operationalData) { + this.configSnapshot = configData; + this.operationalData = operationalData; } @Override public void close() { + closed = true; configSnapshot = null; operationalData = null; } @@ -57,12 +69,13 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { final LogicalDatastoreType store, final YangInstanceIdentifier path) { LOG.debug("ReadOnlyTransaction.read(), store={}, path={}", store, path); - - Preconditions.checkState(configSnapshot != null, "Transaction was closed"); + checkState(!closed, "Transaction has been closed"); if (store == LogicalDatastoreType.OPERATIONAL) { + checkArgument(operationalData != null, "{} reads not supported", store); return operationalData.read(path); } else { + checkArgument(configSnapshot != null, "{} reads not supported", store); return configSnapshot.read(path); } } @@ -73,7 +86,6 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { LOG.debug("ReadOnlyTransaction.exists() store={}, path={}", store, path); ListenableFuture<Boolean> listenableFuture = Futures.transform(read(store, path), IS_NODE_PRESENT); - return Futures.makeChecked(listenableFuture, ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER); } @@ -83,23 +95,25 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction { return this; } + @Nonnull + static ReadOnlyTransaction createOperationalOnly(@Nonnull final ReadableDataManager operationalData) { + return new ReadOnlyTransaction(null, requireNonNull(operationalData)); + } + + @Nonnull + static ReadOnlyTransaction createConfigOnly(@Nonnull final ReadableDataManager configData) { + return new ReadOnlyTransaction(requireNonNull(configData), null); + } + + @Nonnull + static ReadOnlyTransaction create(@Nonnull final ReadableDataManager configData, + @Nonnull final ReadableDataManager operationalData) { + return new ReadOnlyTransaction(requireNonNull(configData), requireNonNull(operationalData)); + } private static final Function<? super Optional<NormalizedNode<?, ?>>, ? extends Boolean> IS_NODE_PRESENT = - new Function<Optional<NormalizedNode<?, ?>>, Boolean>() { - @Nullable - @Override - public Boolean apply(@Nullable final Optional<NormalizedNode<?, ?>> input) { - return input == null - ? Boolean.FALSE - : input.isPresent(); - } - }; + (Function<Optional<NormalizedNode<?, ?>>, Boolean>) input -> input == null ? Boolean.FALSE : input.isPresent(); private static final Function<? super Exception, ReadFailedException> ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER = - new Function<Exception, ReadFailedException>() { - @Override - public ReadFailedException apply(@Nullable final Exception e) { - return new ReadFailedException("Exists failed", e); - } - }; + (Function<Exception, ReadFailedException>) e -> new ReadFailedException("Exists failed", e); }
\ No newline at end of file diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java index 175f22d34..e660ee429 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java @@ -26,15 +26,18 @@ import com.google.common.collect.Collections2; import com.google.common.collect.Multimap; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.read.ReaderRegistry; +import io.fd.honeycomb.v3po.translate.util.write.TransactionMappingContext; import java.util.Collection; import java.util.Map; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState; @@ -61,13 +64,14 @@ import org.slf4j.LoggerFactory; /** * ReadableDataTree implementation for operational data. */ -public final class OperationalDataTree implements ReadableDataTree { - private static final Logger LOG = LoggerFactory.getLogger(OperationalDataTree.class); +public final class ReadableDataTreeDelegator implements ReadableDataManager { + private static final Logger LOG = LoggerFactory.getLogger(ReadableDataTreeDelegator.class); private final BindingNormalizedNodeSerializer serializer; private final ReaderRegistry readerRegistry; private final SchemaContext globalContext; private final DOMDataBroker netconfMonitoringDomDataBrokerDependency; + private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; /** * Creates operational data tree instance. @@ -75,12 +79,16 @@ public final class OperationalDataTree implements ReadableDataTree { * representation. * @param globalContext service for obtaining top level context data from all yang modules. * @param readerRegistry service responsible for translation between DataObjects and data provider. - * @param netconfMonitoringDomDataBrokerDependency + * @param netconfMonitoringDomDataBrokerDependency TODO remove + * @param contextBroker BA broker for context data */ - public OperationalDataTree(@Nonnull BindingNormalizedNodeSerializer serializer, - @Nonnull final SchemaContext globalContext, @Nonnull ReaderRegistry readerRegistry, - final DOMDataBroker netconfMonitoringDomDataBrokerDependency) { + public ReadableDataTreeDelegator(@Nonnull BindingNormalizedNodeSerializer serializer, + @Nonnull final SchemaContext globalContext, + @Nonnull final ReaderRegistry readerRegistry, + @Nonnull final DOMDataBroker netconfMonitoringDomDataBrokerDependency, + @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) { this.netconfMonitoringDomDataBrokerDependency = netconfMonitoringDomDataBrokerDependency; + this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null"); this.globalContext = checkNotNull(globalContext, "globalContext should not be null"); this.serializer = checkNotNull(serializer, "serializer should not be null"); this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null"); @@ -91,16 +99,35 @@ public final class OperationalDataTree implements ReadableDataTree { org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read( @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) { - try(ReadContext ctx = new ReadContextImpl()) { + try(TransactionMappingContext mappingContext = new TransactionMappingContext(contextBroker.newReadWriteTransaction()); + ReadContext ctx = new ReadContextImpl(mappingContext)) { + + final Optional<NormalizedNode<?, ?>> value; if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) { - return Futures.immediateCheckedFuture(readRoot(ctx)); + value = readRoot(ctx); } else { - return Futures.immediateCheckedFuture(readNode(yangInstanceIdentifier, ctx)); + value = readNode(yangInstanceIdentifier, ctx); } + + // Submit context mapping updates + final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult = + ((TransactionMappingContext) ctx.getMappingContext()).submit(); + // Blocking on context data update + contextUpdateResult.checkedGet(); + + return Futures.immediateCheckedFuture(value); + } catch (ReadFailedException e) { return Futures.immediateFailedCheckedFuture( - new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException( - "Failed to read VPP data", e)); + new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException( + "Failed to read VPP data", e)); + } catch (TransactionCommitFailedException e) { + // FIXME revert should probably occur when context is not written successfully, but can that even happen ? + final String msg = "Error while updating mapping context data"; + LOG.error(msg, e); + return Futures.immediateFailedCheckedFuture( + new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(msg, e) + ); } } @@ -108,6 +135,8 @@ public final class OperationalDataTree implements ReadableDataTree { final ReadContext ctx) throws ReadFailedException { // FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/ + // Just delete, dedicated reader from NetconfMonitoringReaderModule takes care of netconf state data + // TODO test connecting with netconf and issuing a get (netconf-state) data should be provided if(yangInstanceIdentifier.getPathArguments().size() > 0 && yangInstanceIdentifier.getPathArguments().get(0).getNodeType().equals(NetconfState.QNAME)) { return readFromNetconfDs(yangInstanceIdentifier); @@ -222,17 +251,29 @@ public final class OperationalDataTree implements ReadableDataTree { } private static final class ReadContextImpl implements ReadContext { - public final Context ctx = new Context(); + + public final ModificationCache ctx = new ModificationCache(); + private final MappingContext mappingContext; + + private ReadContextImpl(final MappingContext mappingContext) { + this.mappingContext = mappingContext; + } @Nonnull @Override - public Context getContext() { + public ModificationCache getModificationCache() { return ctx; } + @Nonnull + @Override + public MappingContext getMappingContext() { + return mappingContext; + } + @Override public void close() { - // Make sure to clear the storage in case some customizer stored it to prevent memory leaks + // Make sure to clear the storage in case some customizer stored a reference to it to prevent memory leaks ctx.close(); } } diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java index fbeba7e07..3644a9fe7 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java @@ -16,6 +16,8 @@ package io.fd.honeycomb.v3po.data.impl; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.CANCELED; import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.COMMITED; import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.FAILED; @@ -26,10 +28,10 @@ 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.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.DataModification; import io.fd.honeycomb.v3po.translate.TranslationException; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -38,7 +40,6 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,30 +48,21 @@ import org.slf4j.LoggerFactory; final class WriteTransaction implements DOMDataWriteTransaction { private static final Logger LOG = LoggerFactory.getLogger(WriteTransaction.class); - private final ModifiableDataTree configDataTree; - private DataTreeModification modification; - private TransactionStatus status; - - WriteTransaction(@Nonnull final ModifiableDataTree configDataTree, - @Nonnull final DataTreeSnapshot configSnapshot) { - this.configDataTree = Preconditions.checkNotNull(configDataTree, "configDataTree should not be null"); - Preconditions.checkNotNull(configSnapshot, "configSnapshot should not be null"); - // initialize transaction state: - modification = configSnapshot.newModification(); - status = NEW; - } - WriteTransaction(@Nonnull final ModifiableDataTree configDataTree) { - this(configDataTree, configDataTree.takeSnapshot()); - } + @Nullable + private DataModification operationalModification; + @Nullable + private DataModification configModification; + private TransactionStatus status = NEW; - private static void checkConfigurationWrite(final LogicalDatastoreType store) { - Preconditions.checkArgument(LogicalDatastoreType.CONFIGURATION == store, "Write is not supported for operational data store"); + private WriteTransaction(@Nullable final DataModification configModification, + @Nullable final DataModification operationalModification) { + this.operationalModification = operationalModification; + this.configModification = configModification; } private void checkIsNew() { Preconditions.checkState(status == NEW, "Transaction was submitted or canceled"); - Preconditions.checkState(modification != null, "DataTree modification should not be null"); } @Override @@ -78,8 +70,21 @@ final class WriteTransaction implements DOMDataWriteTransaction { final NormalizedNode<?, ?> data) { LOG.debug("WriteTransaction.put() store={}, path={}, data={}", store, path, data); checkIsNew(); - checkConfigurationWrite(store); - modification.write(path, data); + handleOperation(store, (modification) -> modification.write(path, data)); + } + + private void handleOperation(final LogicalDatastoreType store, + final java.util.function.Consumer<DataModification> r) { + switch (store) { + case CONFIGURATION: + checkArgument(configModification != null, "Modification of {} is not supported", store); + r.accept(configModification); + break; + case OPERATIONAL: + checkArgument(operationalModification != null, "Modification of {} is not supported", store); + r.accept(operationalModification); + break; + } } @Override @@ -87,8 +92,7 @@ final class WriteTransaction implements DOMDataWriteTransaction { final NormalizedNode<?, ?> data) { LOG.debug("WriteTransaction.merge() store={}, path={}, data={}", store, path, data); checkIsNew(); - checkConfigurationWrite(store); - modification.merge(path, data); + handleOperation(store, (modification) -> modification.merge(path, data)); } @Override @@ -98,7 +102,6 @@ final class WriteTransaction implements DOMDataWriteTransaction { return false; } else { status = CANCELED; - modification = null; return true; } } @@ -107,29 +110,38 @@ final class WriteTransaction implements DOMDataWriteTransaction { public void delete(LogicalDatastoreType store, final YangInstanceIdentifier path) { LOG.debug("WriteTransaction.delete() store={}, path={}", store, path); checkIsNew(); - checkConfigurationWrite(store); - modification.delete(path); + handleOperation(store, (modification) -> modification.delete(path)); } @Override public CheckedFuture<Void, TransactionCommitFailedException> submit() { - LOG.debug("WriteTransaction.submit()"); + LOG.trace("WriteTransaction.submit()"); checkIsNew(); - // seal transaction: - modification.ready(); - status = SUBMITED; - try { - configDataTree.modify(modification); + status = SUBMITED; + + // Validate first to catch any issues before attempting commit + if (configModification != null) { + configModification.validate(); + } + if (operationalModification != null) { + operationalModification.validate(); + } + + if(configModification != null) { + configModification.commit(); + } + if(operationalModification != null) { + operationalModification.commit(); + } + status = COMMITED; } catch (DataValidationFailedException | TranslationException e) { status = FAILED; LOG.error("Failed modify data tree", e); return Futures.immediateFailedCheckedFuture( - new TransactionCommitFailedException("Failed to validate DataTreeModification", e)); - } finally { - modification = null; + new TransactionCommitFailedException("Failed to validate DataTreeModification", e)); } return Futures.immediateCheckedFuture(null); } @@ -144,4 +156,21 @@ final class WriteTransaction implements DOMDataWriteTransaction { public Object getIdentifier() { return this; } + + + @Nonnull + static WriteTransaction createOperationalOnly(@Nonnull final DataModification operationalData) { + return new WriteTransaction(null, requireNonNull(operationalData)); + } + + @Nonnull + static WriteTransaction createConfigOnly(@Nonnull final DataModification configData) { + return new WriteTransaction(requireNonNull(configData), null); + } + + @Nonnull + static WriteTransaction create(@Nonnull final DataModification configData, + @Nonnull final DataModification operationalData) { + return new WriteTransaction(requireNonNull(configData), requireNonNull(operationalData)); + } } diff --git a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java index 86a3f9db4..3e871e09a 100644 --- a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java +++ b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java @@ -1,11 +1,14 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.impl.ConfigDataTree; -import io.fd.honeycomb.v3po.translate.TranslationException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.data.impl.ModifiableDataTreeDelegator; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,14 +38,15 @@ public class ConfigDataTreeModule extends public java.lang.AutoCloseable createInstance() { LOG.debug("ConfigDataTreeModule.createInstance()"); return new CloseableConfigDataTree( - new ConfigDataTree(getSerializerDependency(), getDataTreeDependency(), getWriterRegistryDependency())); + new ModifiableDataTreeDelegator(getSerializerDependency(), getDataTreeDependency(), getWriterRegistryDependency(), + getContextBindingBrokerDependency())); } - private static final class CloseableConfigDataTree implements ModifiableDataTree, AutoCloseable { + private static final class CloseableConfigDataTree implements ModifiableDataManager, AutoCloseable { - private final ConfigDataTree delegate; + private final ModifiableDataTreeDelegator delegate; - CloseableConfigDataTree(final ConfigDataTree delegate) { + CloseableConfigDataTree(final ModifiableDataTreeDelegator delegate) { this.delegate = delegate; } @@ -53,16 +57,15 @@ public class ConfigDataTreeModule extends } @Override - public void modify(final DataTreeModification modification) - throws DataValidationFailedException, TranslationException { - LOG.trace("CloseableConfigDataTree.modify modification={}", modification); - delegate.modify(modification); + public DataModification newModification() { + LOG.trace("CloseableConfigDataTree.newModification"); + return delegate.newModification(); } @Override - public DataTreeSnapshot takeSnapshot() { - LOG.trace("CloseableConfigDataTree.takeSnapshot"); - return delegate.takeSnapshot(); + public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read( + @Nonnull final YangInstanceIdentifier path) { + return delegate.read(path); } } } diff --git a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java index 2fbba75fa..286eaf664 100644 --- a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java +++ b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java @@ -2,8 +2,8 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl. import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.data.impl.OperationalDataTree; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.data.impl.ReadableDataTreeDelegator; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -37,15 +37,15 @@ public class OperationalDataTreeModule extends public java.lang.AutoCloseable createInstance() { LOG.debug("OperationalDataTreeModule.createInstance()"); return new CloseableOperationalDataTree( - new OperationalDataTree(getSerializerDependency(), getSchemaServiceDependency().getGlobalContext(), - getReaderRegistryDependency(), getNetconfMonitoringDomDataBrokerDependency())); + new ReadableDataTreeDelegator(getSerializerDependency(), getSchemaServiceDependency().getGlobalContext(), + getReaderRegistryDependency(), getNetconfMonitoringDomDataBrokerDependency(), getContextBindingBrokerDependency())); } - private static final class CloseableOperationalDataTree implements ReadableDataTree, AutoCloseable { + private static final class CloseableOperationalDataTree implements ReadableDataManager, AutoCloseable { - private final OperationalDataTree delegate; + private final ReadableDataTreeDelegator delegate; - CloseableOperationalDataTree(final OperationalDataTree delegate) { + CloseableOperationalDataTree(final ReadableDataTreeDelegator delegate) { this.delegate = delegate; } diff --git a/v3po/data-impl/src/main/yang/data-impl.yang b/v3po/data-impl/src/main/yang/data-impl.yang index 454e99a66..3af9d8d3f 100644 --- a/v3po/data-impl/src/main/yang/data-impl.yang +++ b/v3po/data-impl/src/main/yang/data-impl.yang @@ -121,6 +121,14 @@ module data-impl { } } + container context-binding-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-async-data-broker; + } + } + } } } @@ -171,6 +179,15 @@ module data-impl { } } + container context-binding-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-async-data-broker; + } + } + } + } } }
\ No newline at end of file diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java index a2908d591..55b92b50b 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java @@ -22,9 +22,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.data.DataModification; import java.util.Map; import org.junit.Before; import org.junit.Test; @@ -42,18 +42,18 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; public class DataBrokerTest { @Mock - private ReadableDataTree operationalData; + private ReadableDataManager operationalData; @Mock - private ModifiableDataTree confiDataTree; + private ModifiableDataManager confiDataTree; @Mock - private DataTreeSnapshot configSnapshot; + private DataModification configSnapshot; private DataBroker broker; @Before public void setUp() { initMocks(this); - when(confiDataTree.takeSnapshot()).thenReturn(configSnapshot); - broker = new DataBroker(operationalData, confiDataTree); + when(confiDataTree.newModification()).thenReturn(configSnapshot); + broker = DataBroker.create(confiDataTree, operationalData); } @Test @@ -64,7 +64,7 @@ public class DataBrokerTest { // verify that read and write transactions use the same config snapshot verify(configSnapshot).read(path); - verify(configSnapshot).newModification(); + verify(confiDataTree).newModification(); } @Test @@ -72,7 +72,7 @@ public class DataBrokerTest { final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction(); // verify that write transactions use config snapshot - verify(configSnapshot).newModification(); + verify(confiDataTree).newModification(); } @Test diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTreeTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java index b7f8b9d2c..fed32da8a 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTreeTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java @@ -25,13 +25,15 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; 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.data.DataTreeSnapshot; +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.v3po.data.DataModification; import io.fd.honeycomb.v3po.translate.TranslationException; import io.fd.honeycomb.v3po.translate.write.WriteContext; import io.fd.honeycomb.v3po.translate.write.WriterRegistry; @@ -42,6 +44,7 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet; @@ -55,9 +58,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -public class ConfigDataTreeTest { +public class ModifiableDataTreeDelegatorTest { @Mock private WriterRegistry writer; @@ -66,14 +68,16 @@ public class ConfigDataTreeTest { @Mock private DataTree dataTree; @Mock - private DataTreeModification modification; + private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification; + @Mock + private DataBroker contextBroker; - private ConfigDataTree configDataTree; + private ModifiableDataTreeManager configDataTree; @Before public void setUp() { initMocks(this); - configDataTree = new ConfigDataTree(serializer, dataTree, writer); + configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker); } @Test @@ -81,17 +85,18 @@ public class ConfigDataTreeTest { final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); when(dataTree.takeSnapshot()).thenReturn(snapshot); + when(snapshot.newModification()).thenReturn(modification); final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); final Optional node = mock(Optional.class); - doReturn(node).when(snapshot).readNode(path); + doReturn(node).when(modification).readNode(path); - final DataTreeSnapshot dataTreeSnapshot = configDataTree.takeSnapshot(); + final DataModification dataTreeSnapshot = configDataTree.newModification(); final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = dataTreeSnapshot.read(path); - verify(dataTree).takeSnapshot(); - verify(snapshot).readNode(path); + verify(dataTree, times(2)).takeSnapshot(); + verify(modification).readNode(path); assertTrue(future.isDone()); assertEquals(node, future.get()); @@ -102,19 +107,29 @@ public class ConfigDataTreeTest { final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); when(dataTree.takeSnapshot()).thenReturn(snapshot); - when(snapshot.newModification()).thenReturn(modification); - final DataTreeSnapshot dataTreeSnapshot = configDataTree.takeSnapshot(); - final DataTreeModification newModification = dataTreeSnapshot.newModification(); - verify(dataTree).takeSnapshot(); - verify(snapshot).newModification(); - - assertEquals(modification, newModification); + final DataModification dataTreeSnapshot = configDataTree.newModification(); + // Snapshot captured twice, so that original data could be provided to translation layer without any possible + // modification + verify(dataTree, times(2)).takeSnapshot(); + verify(snapshot, times(2)).newModification(); } @Test public void testCommitSuccessful() throws Exception { + final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot + snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); + when(dataTree.takeSnapshot()).thenReturn(snapshot); + when(snapshot.newModification()).thenReturn(modification); + final DataTreeCandidate prepare = mock(DataTreeCandidate.class); + doReturn(prepare).when(dataTree).prepare(modification); + + final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock( + org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class); + doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction(); + doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit(); + final DataObject dataBefore = mockDataObject("before", Ethernet.class); final DataObject dataAfter = mockDataObject("after", Ethernet.class); @@ -128,7 +143,9 @@ public class ConfigDataTreeTest { when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter)); // Run the test - configDataTree.modify(modification); + doReturn(rootNode).when(prepare).getRootNode(); + final DataModification dataModification = configDataTree.newModification(); + dataModification.commit(); // Verify all changes were processed: verify(writer).update( @@ -138,6 +155,8 @@ public class ConfigDataTreeTest { // Verify modification was validated verify(dataTree).validate(modification); + // Verify context transaction was finished + verify(ctxTransaction).submit(); } private Map<InstanceIdentifier<?>, DataObject> mapOf(final DataObject dataBefore, final Class<Ethernet> type) { @@ -154,6 +173,13 @@ public class ConfigDataTreeTest { @Test public void testCommitUndoSuccessful() throws Exception { + final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot + snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); + when(dataTree.takeSnapshot()).thenReturn(snapshot); + when(snapshot.newModification()).thenReturn(modification); + final DataTreeCandidate prepare = mock(DataTreeCandidate.class); + doReturn(prepare).when(dataTree).prepare(modification); + // Prepare data changes: final DataObject dataBefore = mockDataObject("before", Ethernet.class); final DataObject dataAfter = mockDataObject("after", Ethernet.class); @@ -177,7 +203,9 @@ public class ConfigDataTreeTest { // Run the test try { - configDataTree.modify(modification); + doReturn(rootNode).when(prepare).getRootNode(); + final DataModification dataModification = configDataTree.newModification(); + dataModification.commit(); } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) { verify(writer).update(anyMap(), anyMap(), any(WriteContext.class)); verify(reverter).revert(); @@ -190,6 +218,13 @@ public class ConfigDataTreeTest { @Test public void testCommitUndoFailed() throws Exception { + final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot + snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class); + when(dataTree.takeSnapshot()).thenReturn(snapshot); + when(snapshot.newModification()).thenReturn(modification); + final DataTreeCandidate prepare = mock(DataTreeCandidate.class); + doReturn(prepare).when(dataTree).prepare(modification); + // Prepare data changes: final DataObject dataBefore = mockDataObject("before", Ethernet.class); final DataObject dataAfter = mockDataObject("after", Ethernet.class); @@ -219,7 +254,9 @@ public class ConfigDataTreeTest { // Run the test try { - configDataTree.modify(modification); + doReturn(rootNode).when(prepare).getRootNode(); + final DataModification dataModification = configDataTree.newModification(); + dataModification.commit(); } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException e) { verify(writer).update(anyMap(), anyMap(), any(WriteContext.class)); verify(reverter).revert(); diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java index 30fdd1392..a13621725 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java @@ -24,8 +24,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.ReadableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.data.DataModification; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -37,16 +37,16 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; public class ReadOnlyTransactionTest { @Mock - private ReadableDataTree operationalData; + private ReadableDataManager operationalData; @Mock - private DataTreeSnapshot configSnapshot; + private DataModification configSnapshot; private ReadOnlyTransaction readOnlyTx; @Before public void setUp() { initMocks(this); - readOnlyTx = new ReadOnlyTransaction(operationalData, configSnapshot); + readOnlyTx = ReadOnlyTransaction.create(configSnapshot, operationalData); } @Test diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTreeTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java index 7e6abcdcd..4492c70a2 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTreeTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java @@ -42,6 +42,7 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; 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.DOMDataBroker; @@ -58,14 +59,14 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -public class OperationalDataTreeTest { +public class ReadableDataTreeDelegatorTest { @Mock private BindingNormalizedNodeSerializer serializer; @Mock private ReaderRegistry reader; - private OperationalDataTree operationalData; + private ReadableDataTreeDelegator operationalData; @Mock private InstanceIdentifier<DataObject> id; @@ -81,16 +82,23 @@ public class OperationalDataTreeTest { private DOMDataBroker netconfMonitoringBroker; @Mock private DOMDataReadOnlyTransaction domDataReadOnlyTransaction; + @Mock + private DataBroker contextBroker; @Before public void setUp() { initMocks(this); - operationalData = new OperationalDataTree(serializer, globalContext, reader, netconfMonitoringBroker); + operationalData = new ReadableDataTreeDelegator(serializer, globalContext, reader, netconfMonitoringBroker, contextBroker); doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class)); doReturn(domDataReadOnlyTransaction).when(netconfMonitoringBroker).newReadOnlyTransaction(); doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(domDataReadOnlyTransaction) .read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)); + + final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock( + org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class); + doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction(); + doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit(); } @Test diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java index d0360dbd8..9cde27d2b 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java @@ -23,12 +23,10 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.data.ModifiableDataTree; -import io.fd.honeycomb.v3po.data.DataTreeSnapshot; +import io.fd.honeycomb.v3po.data.DataModification; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -36,54 +34,48 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; public class WriteTransactionTest { @Mock - private ModifiableDataTree configDataTree; - @Mock - private DataTreeSnapshot configSnapshot; + private DataModification configSnapshot; @Mock private YangInstanceIdentifier path; @Mock private NormalizedNode<?,?> data; - @Mock - private DataTreeModification dataTreeModification; private WriteTransaction writeTx; @Before public void setUp() { initMocks(this); - when(configSnapshot.newModification()).thenReturn(dataTreeModification); - writeTx = new WriteTransaction(configDataTree, configSnapshot); + writeTx = WriteTransaction.createConfigOnly(configSnapshot); } @Test public void testPut() { writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); - verify(dataTreeModification).write(path, data); + verify(configSnapshot).write(path, data); } @Test(expected = IllegalArgumentException.class) public void testPutOperational() { writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data); - verify(dataTreeModification).write(path, data); + verify(configSnapshot).write(path, data); } @Test(expected = IllegalStateException.class) public void testOnFinishedTx() { writeTx.submit(); writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); - verify(dataTreeModification).write(path, data); + verify(configSnapshot).write(path, data); } @Test public void testMerge() { writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, data); - verify(dataTreeModification).merge(path, data); + verify(configSnapshot).merge(path, data); } @Test @@ -100,19 +92,19 @@ public class WriteTransactionTest { @Test public void testDelete() { writeTx.delete(LogicalDatastoreType.CONFIGURATION, path); - verify(dataTreeModification).delete(path); + verify(configSnapshot).delete(path); } @Test public void testSubmit() throws Exception { writeTx.submit(); - verify(dataTreeModification).ready(); - verify(configDataTree).modify(dataTreeModification); + verify(configSnapshot).validate(); + verify(configSnapshot).commit(); } @Test public void testSubmitFailed() throws Exception { - doThrow(mock(DataValidationFailedException.class)).when(configDataTree).modify(dataTreeModification); + doThrow(mock(DataValidationFailedException.class)).when(configSnapshot).commit(); final CheckedFuture<Void, TransactionCommitFailedException> future = writeTx.submit(); try { future.get(); diff --git a/v3po/impl/src/main/config/context-datatree-config.xml b/v3po/impl/src/main/config/context-datatree-config.xml index c9f9902f3..13da9ec65 100644 --- a/v3po/impl/src/main/config/context-datatree-config.xml +++ b/v3po/impl/src/main/config/context-datatree-config.xml @@ -58,7 +58,40 @@ </schema-service> <persist-file-path>etc/opendaylight/honeycomb/context.json</persist-file-path> </module> + + <!-- DOM Data Broker for context data --> + <module> + <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:v3po:impl">prefix:honeycomb-context-dom-data-broker</type> + <name>honeycomb-context-data-broker</name> + <!-- With persistence --> + <context-data-tree> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type> + <name>inmemory-persisted-context-data-tree</name> + </context-data-tree> + </module> + + <!-- BA Data Broker for context data --> + <module> + <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type> + <name>honeycomb-context-binding-data-broker</name> + <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl"> + <dom-async-broker> + <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type> + <name>honeycomb-context-data-broker</name> + </dom-async-broker> + <schema-service> + <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type> + <name>yang-schema-service</name> + </schema-service> + <binding-mapping-service> + <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type> + <name>runtime-mapping-singleton</name> + </binding-mapping-service> + </binding-forwarded-data-broker> + </module> </modules> + + <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"> <service> <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type> @@ -73,6 +106,22 @@ </provider> </instance> </service> + + <service> + <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type> + <instance> + <name>honeycomb-context-data-broker</name> + <provider>/modules/module[type='honeycomb-context-dom-data-broker'][name='honeycomb-context-data-broker']</provider> + </instance> + </service> + + <service> + <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type> + <instance> + <name>honeycomb-context-binding-data-broker</name> + <provider>/modules/module[type='binding-forwarded-data-broker'][name='honeycomb-context-binding-data-broker']</provider> + </instance> + </service> </services> </data> </configuration> diff --git a/v3po/impl/src/main/config/default-config.xml b/v3po/impl/src/main/config/default-config.xml index 89e1f7ce2..7106c78ce 100644 --- a/v3po/impl/src/main/config/default-config.xml +++ b/v3po/impl/src/main/config/default-config.xml @@ -88,6 +88,10 @@ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry</type> <name>write-registry</name> </writer-registry> + <context-binding-broker> + <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type> + <name>honeycomb-context-binding-data-broker</name> + </context-binding-broker> </module> <!-- FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/ Move to netconf-north-config--> @@ -152,6 +156,10 @@ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type> <name>netconf-inmemory-data-broker</name> </netconf-monitoring-dom-data-broker> + <context-binding-broker> + <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type> + <name>honeycomb-context-binding-data-broker</name> + </context-binding-broker> </module> <!-- DOM data broker which provides transaction functionality for HC using BI format--> @@ -233,6 +241,10 @@ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry</type> <name>noop-writer-registry</name> </writer-registry> + <context-binding-broker> + <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type> + <name>honeycomb-context-binding-data-broker</name> + </context-binding-broker> </module> <!-- DOM data broker for config initialization --> <module> diff --git a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java new file mode 100644 index 000000000..1d1fa5323 --- /dev/null +++ b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java @@ -0,0 +1,26 @@ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; + +import io.fd.honeycomb.v3po.data.impl.DataBroker; +import io.fd.honeycomb.v3po.data.impl.ModifiableDataTreeManager; + +public class ContextDataBrokerModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractContextDataBrokerModule { + + public ContextDataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public ContextDataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.ContextDataBrokerModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + return DataBroker.create(new ModifiableDataTreeManager(getContextDataTreeDependency())); + } + +} diff --git a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java new file mode 100644 index 000000000..360f5f472 --- /dev/null +++ b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: v3po-impl yang module local name: honeycomb-context-dom-data-broker +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Mon May 16 15:27:12 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; +public class ContextDataBrokerModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractContextDataBrokerModuleFactory { + +} diff --git a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java index 24e34c0a9..8aa3d64d9 100644 --- a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java +++ b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java @@ -1,19 +1,6 @@ package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; import io.fd.honeycomb.v3po.data.impl.DataBroker; -import java.util.Map; -import javax.annotation.Nonnull; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension; -import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; -import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,63 +29,6 @@ public class DataBrokerModule extends @Override public java.lang.AutoCloseable createInstance() { LOG.debug("DataBrokerModule.createInstance()"); - return new CloseableDataBroker( - new DataBroker(getOperationalDataTreeDependency(), getConfigDataTreeDependency())); - } - - private static final class CloseableDataBroker implements AutoCloseable, DOMDataBroker { - - private final DataBroker delegate; - - CloseableDataBroker(final DataBroker delegate) { - this.delegate = delegate; - } - - @Override - public void close() throws Exception { - LOG.debug("CloseableDataBroker.close()"); - // NOP - } - - @Override - public DOMDataReadOnlyTransaction newReadOnlyTransaction() { - LOG.trace("CloseableDataBroker.newReadOnlyTransaction()"); - return delegate.newReadOnlyTransaction(); - } - - @Override - public DOMDataReadWriteTransaction newReadWriteTransaction() { - LOG.trace("CloseableDataBroker.newReadWriteTransaction()"); - return delegate.newReadWriteTransaction(); - } - - @Override - public DOMDataWriteTransaction newWriteOnlyTransaction() { - LOG.trace("CloseableDataBroker.newWriteOnlyTransaction()"); - return delegate.newWriteOnlyTransaction(); - } - - @Override - public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store, - final YangInstanceIdentifier path, - final DOMDataChangeListener listener, - final DataChangeScope triggeringScope) { - LOG.trace("CloseableDataBroker.createTransactionChain store={}, path={}, listener={}, triggeringScope={}", - store, path, listener, triggeringScope); - return delegate.registerDataChangeListener(store, path, listener, triggeringScope); - } - - @Override - public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) { - LOG.trace("CloseableDataBroker.createTransactionChain listener={}", listener); - return delegate.createTransactionChain(listener); - } - - @Nonnull - @Override - public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() { - LOG.trace("CloseableDataBroker.getSupportedExtensions()"); - return delegate.getSupportedExtensions(); - } + return DataBroker.create(getConfigDataTreeDependency(), getOperationalDataTreeDependency()); } } diff --git a/v3po/impl/src/main/yang/v3po-impl.yang b/v3po/impl/src/main/yang/v3po-impl.yang index 30b70f342..3f7af6185 100644 --- a/v3po/impl/src/main/yang/v3po-impl.yang +++ b/v3po/impl/src/main/yang/v3po-impl.yang @@ -101,6 +101,28 @@ module v3po-impl { } } + identity honeycomb-context-dom-data-broker { + base config:module-type; + config:provided-service dom:dom-async-data-broker; + config:java-name-prefix ContextDataBroker; + description "DomBroker on top of context data-tree"; + } + + augment "/config:modules/config:module/config:configuration" { + case honeycomb-context-dom-data-broker { + when "/config:modules/config:module/config:type = 'honeycomb-context-dom-data-broker'"; + + container context-data-tree { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dapi:data-tree; + } + } + } + } + } + identity netconf-monitoring-reader { base config:module-type; config:provided-service tapi:honeycomb-reader; diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java new file mode 100644 index 000000000..cff766e2a --- /dev/null +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java @@ -0,0 +1,66 @@ +/* + * 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.translate; + +import com.google.common.base.Optional; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Mapping context is persisted storage where mapping matadata are stored. + * A snapshot is created for each transaction to provide consistent view over context data. + * After a transaction is successfully finished, objects added to this context are propagated to backing storage. + */ +public interface MappingContext extends AutoCloseable { + + /** + * Read any mapping context data + * + * @param currentId Id of an object to read + * + * @return Relevant mapping context data + */ + <T extends DataObject> Optional<T> read(@Nonnull final InstanceIdentifier<T> currentId); + + /** + * Delete the node at specified path. + * + * @param path Node path + */ + void delete(InstanceIdentifier<?> path); + + /** + * Merge the specified data with the currently-present data + * at specified path. + * + * @param path Node path + * @param data Data to be merged + */ + <T extends DataObject> void merge(InstanceIdentifier<T> path, T data); + + /** + * Replace the data at specified path with supplied data. + * + * @param path Node path + * @param data New node data + */ + <T extends DataObject> void put(InstanceIdentifier<T> path, T data); + + @Override + void close(); +} diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/Context.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java index b60963678..cb2d4fde0 100644 --- a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/Context.java +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java @@ -22,11 +22,11 @@ import java.util.HashMap; /** * Simple context class that provides transient storage during one or more read/write operations */ -public class Context implements AutoCloseable { +public class ModificationCache implements AutoCloseable { protected final HashMap<Object, Object> map; - public Context() { + public ModificationCache() { map = Maps.newHashMap(); } diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java new file mode 100644 index 000000000..2c039aba0 --- /dev/null +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java @@ -0,0 +1,44 @@ +/* + * 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.translate; + +import javax.annotation.Nonnull; + +/** + * Common context for both writes and reads + */ +public interface ModificationContext extends AutoCloseable { + + /** + * Get key value transient storage for customizers. Is cleared for each new transaction. + * + * @return Context for customizers + */ + @Nonnull + ModificationCache getModificationCache(); + + /** + * Get persistent storage for mapping context. This context survives a modification. + * + * @return Mapping context accessor + */ + @Nonnull + MappingContext getMappingContext(); + + @Override + void close(); +} diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java index 6b1473548..e3ddd420c 100644 --- a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java @@ -16,22 +16,11 @@ package io.fd.honeycomb.v3po.translate.read; -import io.fd.honeycomb.v3po.translate.Context; -import javax.annotation.Nonnull; +import io.fd.honeycomb.v3po.translate.ModificationContext; /** - * Read Context + * Context providing information about current state of DataTree to readers */ -public interface ReadContext extends AutoCloseable { +public interface ReadContext extends ModificationContext { - /** - * Get key value storage for customizers - * - * @return Context for customizers - */ - @Nonnull - Context getContext(); - - @Override - void close(); } diff --git a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java index bb0b33145..3433b3f34 100644 --- a/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java +++ b/v3po/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java @@ -18,7 +18,7 @@ package io.fd.honeycomb.v3po.translate.write; import com.google.common.annotations.Beta; import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.ModificationContext; import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -27,34 +27,24 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; * Context providing information about current state of DataTree to writers */ @Beta -public interface WriteContext extends AutoCloseable { +public interface WriteContext extends ModificationContext { /** - * Read any data object before current modification was applied + * Read any config data object before current modification was applied * * @param currentId Id of an object to read * * @return Data before the modification was applied */ - Optional<DataObject> readBefore(@Nonnull final InstanceIdentifier<? extends DataObject> currentId); + <T extends DataObject> Optional<T> readBefore(@Nonnull final InstanceIdentifier<T> currentId); /** - * Read any data object from current modification + * Read any config data object from current modification * * @param currentId Id of an object to read * * @return Data from the modification */ - Optional<DataObject> readAfter(@Nonnull final InstanceIdentifier<? extends DataObject> currentId); + <T extends DataObject> Optional<T> readAfter(@Nonnull final InstanceIdentifier<T> currentId); - /** - * Get key value storage for customizers - * - * @return Context for customizers - */ - @Nonnull - Context getContext(); - - @Override - void close(); } diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeChildReader.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeChildReader.java index 1984cd86d..89f9f5675 100644 --- a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeChildReader.java +++ b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeChildReader.java @@ -93,7 +93,7 @@ public final class CompositeChildReader<C extends DataObject, B extends Builder< */ public CompositeChildReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final ChildReaderCustomizer<C, B> customizer) { - this(managedDataObjectType, RWUtils.<C>emptyChildReaderList(), RWUtils.<C>emptyAugReaderList(), + this(managedDataObjectType, RWUtils.emptyChildReaderList(), RWUtils.emptyAugReaderList(), customizer); } @@ -112,7 +112,7 @@ public final class CompositeChildReader<C extends DataObject, B extends Builder< protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder, @Nonnull final ReadContext ctx) throws ReadFailedException { - customizer.readCurrentAttributes(id, builder, ctx.getContext()); + customizer.readCurrentAttributes(id, builder, ctx); } @Override diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeListReader.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeListReader.java index 4c84c3afe..7c438f669 100644 --- a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeListReader.java +++ b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeListReader.java @@ -124,7 +124,7 @@ public final class CompositeListReader<C extends DataObject & Identifiable<K>, K public List<C> readList(@Nonnull final InstanceIdentifier<C> id, @Nonnull final ReadContext ctx) throws ReadFailedException { LOG.trace("{}: Reading all list entries", this); - final List<K> allIds = customizer.getAllIds(id, ctx.getContext()); + final List<K> allIds = customizer.getAllIds(id, ctx); LOG.debug("{}: Reading list entries for: {}", this, allIds); final ArrayList<C> allEntries = new ArrayList<>(allIds.size()); @@ -146,7 +146,7 @@ public final class CompositeListReader<C extends DataObject & Identifiable<K>, K protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder, @Nonnull final ReadContext ctx) throws ReadFailedException { - customizer.readCurrentAttributes(id, builder, ctx.getContext()); + customizer.readCurrentAttributes(id, builder, ctx); } @Override diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeRootReader.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeRootReader.java index 0abae70be..ea157aafd 100644 --- a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeRootReader.java +++ b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/CompositeRootReader.java @@ -99,7 +99,7 @@ public final class CompositeRootReader<C extends DataObject, B extends Builder<C @Override protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder, @Nonnull final ReadContext ctx) throws ReadFailedException { - customizer.readCurrentAttributes(id, builder, ctx.getContext()); + customizer.readCurrentAttributes(id, builder, ctx); } @Override diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java index 580910e8c..8b42a6c6f 100644 --- a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java +++ b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/AbstractCompositeWriter.java @@ -232,7 +232,7 @@ public abstract class AbstractCompositeWriter<D extends DataObject> implements W // If there's no dedicated writer, use write current // But we need current data after to do so final InstanceIdentifier<D> currentId = RWUtils.cutId(id, getManagedDataObjectType()); - Optional<DataObject> currentDataAfter = ctx.readAfter(currentId); + Optional<D> currentDataAfter = ctx.readAfter(currentId); LOG.debug("{}: Dedicated subtree writer missing for: {}. Writing current.", this, RWUtils.getNextId(id, getManagedDataObjectType()).getType(), currentDataAfter); writeCurrent(currentId, castToManaged(currentDataAfter.get()), ctx); @@ -260,8 +260,8 @@ public abstract class AbstractCompositeWriter<D extends DataObject> implements W private void updateSubtreeFromCurrent(final InstanceIdentifier<? extends DataObject> id, final WriteContext ctx) throws WriteFailedException { final InstanceIdentifier<D> currentId = RWUtils.cutId(id, getManagedDataObjectType()); - Optional<DataObject> currentDataBefore = ctx.readBefore(currentId); - Optional<DataObject> currentDataAfter = ctx.readAfter(currentId); + Optional<D> currentDataBefore = ctx.readBefore(currentId); + Optional<D> currentDataAfter = ctx.readAfter(currentId); LOG.debug("{}: Dedicated subtree writer missing for: {}. Updating current without subtree", this, RWUtils.getNextId(id, getManagedDataObjectType()).getType(), currentDataAfter); updateCurrent((InstanceIdentifier<D>) id, castToManaged(currentDataBefore.orNull()), diff --git a/v3po/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java b/v3po/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java index 857743e02..4a2e02cfb 100644 --- a/v3po/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java +++ b/v3po/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java @@ -17,7 +17,7 @@ package io.fd.honeycomb.v3po.translate.spi.read; import com.google.common.annotations.Beta; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import java.util.List; import javax.annotation.Nonnull; @@ -46,7 +46,7 @@ public interface ListReaderCustomizer<C extends DataObject & Identifiable<K>, K * @throws ReadFailedException if the list of IDs could not be read */ @Nonnull - List<K> getAllIds(@Nonnull final InstanceIdentifier<C> id, @Nonnull final Context context) throws + List<K> getAllIds(@Nonnull final InstanceIdentifier<C> id, @Nonnull final ReadContext context) throws ReadFailedException; // TODO does it make sense with vpp APIs ? Should we replace it with a simple readAll ? diff --git a/v3po/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/RootReaderCustomizer.java b/v3po/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/RootReaderCustomizer.java index da599eb58..f0cb76860 100644 --- a/v3po/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/RootReaderCustomizer.java +++ b/v3po/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/RootReaderCustomizer.java @@ -17,8 +17,8 @@ package io.fd.honeycomb.v3po.translate.spi.read; import com.google.common.annotations.Beta; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; -import io.fd.honeycomb.v3po.translate.Context; import javax.annotation.Nonnull; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -45,8 +45,9 @@ public interface RootReaderCustomizer<C extends DataObject, B extends Builder<C> * * @param id id of current data object * @param builder builder for creating read value + * @param ctx * @throws ReadFailedException if read was unsuccessful */ void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder, - @Nonnull final Context ctx) throws ReadFailedException; + @Nonnull final ReadContext ctx) throws ReadFailedException; } diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java index 3b703da3f..cf494efbe 100644 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java @@ -16,7 +16,7 @@ package io.fd.honeycomb.v3po.translate.util.read; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer; import org.opendaylight.yangtools.concepts.Builder; @@ -27,7 +27,7 @@ public abstract class NoopReaderCustomizer<C extends DataObject, B extends Build RootReaderCustomizer<C, B> { @Override - public void readCurrentAttributes(InstanceIdentifier<C> id, final B builder, final Context context) throws + public void readCurrentAttributes(InstanceIdentifier<C> id, final B builder, final ReadContext context) throws ReadFailedException { // Noop } diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionMappingContext.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionMappingContext.java new file mode 100644 index 000000000..60f4282f5 --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionMappingContext.java @@ -0,0 +1,75 @@ +/* + * 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.translate.util.write; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.translate.MappingContext; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +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.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Binding Transaction backed mapping context + */ +public class TransactionMappingContext implements MappingContext { + + private final ReadWriteTransaction readWriteTransaction; + + // TODO make async + + public TransactionMappingContext(final ReadWriteTransaction readWriteTransaction) { + this.readWriteTransaction = readWriteTransaction; + } + + @Override + public <T extends DataObject> Optional<T> read(@Nonnull final InstanceIdentifier<T> currentId) { + try { + return readWriteTransaction.read(LogicalDatastoreType.OPERATIONAL, currentId).checkedGet(); + } catch (ReadFailedException e) { + throw new IllegalStateException("Unable to perform read", e); + } + } + + @Override + public void delete(final InstanceIdentifier<?> path) { + readWriteTransaction.delete(LogicalDatastoreType.OPERATIONAL, path); + } + + @Override + public <T extends DataObject> void merge(final InstanceIdentifier<T> path, T data) { + readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, path, data, true); + } + + @Override + public <T extends DataObject> void put(final InstanceIdentifier<T> path, T data) { + readWriteTransaction.put(LogicalDatastoreType.OPERATIONAL, path, data, true); + } + + public CheckedFuture<Void, TransactionCommitFailedException> submit() { + return readWriteTransaction.submit(); + } + + @Override + public void close() { + readWriteTransaction.cancel(); + } +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java index 37756d5b8..20bbf19e0 100644 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java @@ -16,9 +16,12 @@ package io.fd.honeycomb.v3po.translate.util.write; +import static com.google.common.base.Preconditions.checkState; + import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.write.WriteContext; import java.util.Map; import javax.annotation.Nonnull; @@ -38,27 +41,37 @@ public final class TransactionWriteContext implements WriteContext { private final DOMDataReadOnlyTransaction beforeTx; private final DOMDataReadOnlyTransaction afterTx; - private final Context ctx; + private final ModificationCache ctx; private final BindingNormalizedNodeSerializer serializer; + private MappingContext mappingContext; public TransactionWriteContext(final BindingNormalizedNodeSerializer serializer, final DOMDataReadOnlyTransaction beforeTx, - final DOMDataReadOnlyTransaction afterTx) { + final DOMDataReadOnlyTransaction afterTx, + final MappingContext mappingContext) { this.serializer = serializer; + // TODO do we have a BA transaction adapter ? If so, use it here and don't pass serializer this.beforeTx = beforeTx; this.afterTx = afterTx; - this.ctx = new Context(); + this.mappingContext = mappingContext; + this.ctx = new ModificationCache(); } // TODO make this asynchronous @Override - public Optional<DataObject> readBefore(@Nonnull final InstanceIdentifier<? extends DataObject> currentId) { + public <T extends DataObject> Optional<T> readBefore(@Nonnull final InstanceIdentifier<T> currentId) { return read(currentId, beforeTx); } - private Optional<DataObject> read(final InstanceIdentifier<? extends DataObject> currentId, - final DOMDataReadOnlyTransaction tx) { + @Override + public <T extends DataObject> Optional<T> readAfter(@Nonnull final InstanceIdentifier<T> currentId) { + return read(currentId, afterTx); + } + + + private <T extends DataObject> Optional<T> read(final InstanceIdentifier<T> currentId, + final DOMDataReadOnlyTransaction tx) { final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(currentId); final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read = @@ -75,20 +88,25 @@ public final class TransactionWriteContext implements WriteContext { final NormalizedNode<?, ?> data = optional.get(); final Map.Entry<InstanceIdentifier<?>, DataObject> entry = serializer.fromNormalizedNode(path, data); - return Optional.of(entry.getValue()); + final Class<T> targetType = currentId.getTargetType(); + checkState(targetType.isAssignableFrom(entry.getValue().getClass()), + "Unexpected data object type, should be: %s, but was: %s", targetType, entry.getValue().getClass()); + return Optional.of(targetType.cast(entry.getValue())); } catch (ReadFailedException e) { throw new IllegalStateException("Unable to perform read", e); } } + @Nonnull @Override - public Optional<DataObject> readAfter(@Nonnull final InstanceIdentifier<? extends DataObject> currentId) { - return read(currentId, afterTx); + public ModificationCache getModificationCache() { + return ctx; } + @Nonnull @Override - public Context getContext() { - return ctx; + public MappingContext getMappingContext() { + return mappingContext; } /** @@ -97,5 +115,7 @@ public final class TransactionWriteContext implements WriteContext { @Override public void close() { ctx.close(); + beforeTx.close(); + afterTx.close(); } } diff --git a/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java index 03dc0c200..8a3bfbdd9 100644 --- a/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java +++ b/v3po/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java @@ -29,7 +29,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext; import java.util.Map; import org.junit.Before; @@ -43,7 +44,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev 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.binding.data.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; @@ -62,13 +62,15 @@ public class TransactionWriteContextTest { private Optional<org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode<?, ?>> optional; @Mock private Map.Entry entry; + @Mock + private MappingContext contextBroker; private TransactionWriteContext transactionWriteContext; @Before public void setUp() { initMocks(this); - transactionWriteContext = new TransactionWriteContext(serializer, beforeTx, afterTx); + transactionWriteContext = new TransactionWriteContext(serializer, beforeTx, afterTx, contextBroker); } @Test @@ -80,7 +82,7 @@ public class TransactionWriteContextTest { final InstanceIdentifier<BridgeDomain> instanceId = InstanceIdentifier.create(Vpp.class).child(BridgeDomains.class).child(BridgeDomain.class); - final Optional<DataObject> dataObjects = transactionWriteContext.readBefore(instanceId); + final Optional<BridgeDomain> dataObjects = transactionWriteContext.readBefore(instanceId); assertNotNull(dataObjects); assertFalse(dataObjects.isPresent()); @@ -101,9 +103,9 @@ public class TransactionWriteContextTest { BridgeDomains.QNAME).node(BridgeDomain.QNAME).build(); when(serializer.toYangInstanceIdentifier(any(InstanceIdentifier.class))).thenReturn(yangId); when(serializer.fromNormalizedNode(eq(yangId), any(NormalizedNode.class))).thenReturn(entry); - when(entry.getValue()).thenReturn(mock(DataObject.class)); + when(entry.getValue()).thenReturn(mock(BridgeDomain.class)); - final Optional<DataObject> dataObjects = transactionWriteContext.readBefore(instanceId); + final Optional<BridgeDomain> dataObjects = transactionWriteContext.readBefore(instanceId); assertNotNull(dataObjects); assertTrue(dataObjects.isPresent()); @@ -127,12 +129,12 @@ public class TransactionWriteContextTest { @Test public void testGetContext() throws Exception { - assertNotNull(transactionWriteContext.getContext()); + assertNotNull(transactionWriteContext.getModificationCache()); } @Test public void testClose() throws Exception { - final Context context = transactionWriteContext.getContext(); + final ModificationCache context = transactionWriteContext.getModificationCache(); transactionWriteContext.close(); // TODO verify context was closed } diff --git a/v3po/v3po2vpp/src/main/config/default-config.xml b/v3po/v3po2vpp/src/main/config/default-config.xml index 02b0b7b89..be45a291b 100644 --- a/v3po/v3po2vpp/src/main/config/default-config.xml +++ b/v3po/v3po2vpp/src/main/config/default-config.xml @@ -32,19 +32,11 @@ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:util">prefix:naming-context-impl</type> <name>interface-context</name> <artificial-name-prefix>interface-</artificial-name-prefix> - <context-data-tree> - <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type> - <name>inmemory-persisted-context-data-tree</name> - </context-data-tree> </module> <module> <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:util">prefix:naming-context-impl</type> <name>bridge-domain-context</name> <artificial-name-prefix>bridge-domain-</artificial-name-prefix> - <context-data-tree> - <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type> - <name>inmemory-persisted-context-data-tree</name> - </context-data-tree> </module> <module> diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceCustomizer.java index e6ed62acf..eb6426241 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceCustomizer.java @@ -57,7 +57,7 @@ public class InterfaceCustomizer extends FutureJVppCustomizer implements ListWri throws WriteFailedException { try { - setInterface(id, dataAfter); + setInterface(id, dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Update of VppInterfaceAugment failed", e); throw new WriteFailedException.CreateFailedException(id, dataAfter, e); @@ -72,7 +72,7 @@ public class InterfaceCustomizer extends FutureJVppCustomizer implements ListWri throws WriteFailedException.UpdateFailedException { try { - updateInterface(id, dataBefore, dataAfter); + updateInterface(id, dataBefore, dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Update of VppInterfaceAugment failed", e); throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); @@ -95,25 +95,25 @@ public class InterfaceCustomizer extends FutureJVppCustomizer implements ListWri } - private void setInterface(final InstanceIdentifier<Interface> id, final Interface swIf) + private void setInterface(final InstanceIdentifier<Interface> id, final Interface swIf, + final WriteContext writeContext) throws VppApiInvocationException, WriteFailedException { LOG.debug("Setting interface: {} to: {}", id, swIf); - setInterfaceAttributes(swIf, swIf.getName()); + setInterfaceAttributes(swIf, swIf.getName(), writeContext); } - private void setInterfaceAttributes(final Interface swIf, final String swIfName) + private void setInterfaceAttributes(final Interface swIf, final String swIfName, final WriteContext writeContext) throws VppApiInvocationException { - setInterfaceFlags(swIfName, interfaceContext.getIndex(swIfName), + setInterfaceFlags(swIfName, interfaceContext.getIndex(swIfName, writeContext.getMappingContext()), swIf.isEnabled() ? (byte) 1 : (byte) 0); } private void updateInterface(final InstanceIdentifier<Interface> id, final Interface dataBefore, - final Interface dataAfter) throws VppApiInvocationException { + final Interface dataAfter, final WriteContext writeContext) throws VppApiInvocationException { LOG.debug("Updating interface:{} to: {}", id, dataAfter); - - setInterfaceAttributes(dataAfter, dataAfter.getName()); + setInterfaceAttributes(dataAfter, dataAfter.getName(), writeContext); } private void setInterfaceFlags(final String swIfName, final int swIfIndex, final byte enabled) diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/L2Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/L2Customizer.java index fc43cdb7e..5aea5de2b 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/L2Customizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/L2Customizer.java @@ -69,9 +69,9 @@ public class L2Customizer extends FutureJVppCustomizer implements ChildWriterCus throws WriteFailedException { final String ifcName = id.firstKeyOf(Interface.class).getName(); - final int swIfc = interfaceContext.getIndex(ifcName); + final int swIfc = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); try { - setL2(id, swIfc, ifcName, dataAfter); + setL2(id, swIfc, ifcName, dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Write of L2 failed", e); throw new WriteFailedException.CreateFailedException(id, dataAfter, e); @@ -84,10 +84,10 @@ public class L2Customizer extends FutureJVppCustomizer implements ChildWriterCus throws WriteFailedException { final String ifcName = id.firstKeyOf(Interface.class).getName(); - final int swIfc = interfaceContext.getIndex(ifcName); + final int swIfc = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); // TODO handle update properly (if possible) try { - setL2(id, swIfc, ifcName, dataAfter); + setL2(id, swIfc, ifcName, dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Update of L2 failed", e); throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); @@ -100,21 +100,22 @@ public class L2Customizer extends FutureJVppCustomizer implements ChildWriterCus // TODO implement delete (if possible) } - private void setL2(final InstanceIdentifier<L2> id, final int swIfIndex, final String ifcName, final L2 vppL2) + private void setL2(final InstanceIdentifier<L2> id, final int swIfIndex, final String ifcName, final L2 vppL2, + final WriteContext writeContext) throws VppApiInvocationException, WriteFailedException { LOG.debug("Setting L2 for interface: {}", ifcName); // Nothing besides interconnection here - setInterconnection(id, swIfIndex, ifcName, vppL2); + setInterconnection(id, swIfIndex, ifcName, vppL2, writeContext); } private void setInterconnection(final InstanceIdentifier<L2> id, final int swIfIndex, final String ifcName, - final L2 vppL2) + final L2 vppL2, final WriteContext writeContext) throws VppApiInvocationException, WriteFailedException { Interconnection ic = vppL2.getInterconnection(); if (ic instanceof XconnectBased) { - setXconnectBasedL2(swIfIndex, ifcName, (XconnectBased) ic); + setXconnectBasedL2(swIfIndex, ifcName, (XconnectBased) ic, writeContext); } else if (ic instanceof BridgeBased) { - setBridgeBasedL2(swIfIndex, ifcName, (BridgeBased) ic); + setBridgeBasedL2(swIfIndex, ifcName, (BridgeBased) ic, writeContext); } else { // FIXME how does choice extensibility work // FIXME it is not even possible to create a dedicated customizer for Interconnection, since it's not a DataObject @@ -125,7 +126,8 @@ public class L2Customizer extends FutureJVppCustomizer implements ChildWriterCus } } - private void setBridgeBasedL2(final int swIfIndex, final String ifcName, final BridgeBased bb) + private void setBridgeBasedL2(final int swIfIndex, final String ifcName, final BridgeBased bb, + final WriteContext writeContext) throws VppApiInvocationException { LOG.debug("Setting bridge based interconnection(bridge-domain={}) for interface: {}", @@ -133,7 +135,7 @@ public class L2Customizer extends FutureJVppCustomizer implements ChildWriterCus String bdName = bb.getBridgeDomain(); - int bdId = bridgeDomainContext.getIndex(bdName); + int bdId = bridgeDomainContext.getIndex(bdName, writeContext.getMappingContext()); checkArgument(bdId > 0, "Unable to set Interconnection for Interface: %s, bridge domain: %s does not exist", ifcName, bdName); @@ -168,14 +170,15 @@ public class L2Customizer extends FutureJVppCustomizer implements ChildWriterCus return swInterfaceSetL2Bridge; } - private void setXconnectBasedL2(final int swIfIndex, final String ifcName, final XconnectBased ic) + private void setXconnectBasedL2(final int swIfIndex, final String ifcName, final XconnectBased ic, + final WriteContext writeContext) throws VppApiInvocationException { String outSwIfName = ic.getXconnectOutgoingInterface(); LOG.debug("Setting xconnect based interconnection(outgoing ifc={}) for interface: {}", outSwIfName, ifcName); - int outSwIfIndex = interfaceContext.getIndex(outSwIfName); + int outSwIfIndex = interfaceContext.getIndex(outSwIfName, writeContext.getMappingContext()); checkArgument(outSwIfIndex > 0, "Unable to set Interconnection for Interface: %s, outgoing interface: %s does not exist", ifcName, outSwIfIndex); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/RoutingCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/RoutingCustomizer.java index 4d17ba098..bf355e479 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/RoutingCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/RoutingCustomizer.java @@ -60,7 +60,7 @@ public class RoutingCustomizer extends FutureJVppCustomizer implements ChildWrit throws WriteFailedException.CreateFailedException { try { - setRouting(id.firstKeyOf(Interface.class).getName(), dataAfter); + setRouting(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Update of Routing failed", e); throw new WriteFailedException.CreateFailedException(id, dataAfter, e); @@ -75,7 +75,7 @@ public class RoutingCustomizer extends FutureJVppCustomizer implements ChildWrit try { // TODO handle updates properly - setRouting(id.firstKeyOf(Interface.class).getName(), dataAfter); + setRouting(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Update of Routing failed", e); throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); @@ -88,8 +88,8 @@ public class RoutingCustomizer extends FutureJVppCustomizer implements ChildWrit // TODO implement delete } - private void setRouting(final String name, final Routing rt) throws VppApiInvocationException { - final int swIfc = interfaceContext.getIndex(name); + private void setRouting(final String name, final Routing rt, final WriteContext writeContext) throws VppApiInvocationException { + final int swIfc = interfaceContext.getIndex(name, writeContext.getMappingContext()); LOG.debug("Setting routing for interface: {}, {}. Routing: {}", name, swIfc, rt); int vrfId = (rt != null) diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/SubInterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/SubInterfaceCustomizer.java index ecf2337e8..fb47b6a9f 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/SubInterfaceCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/SubInterfaceCustomizer.java @@ -75,15 +75,16 @@ public class SubInterfaceCustomizer extends AbstractInterfaceTypeCustomizer<SubI @Nonnull final SubInterface dataAfter, @Nonnull final WriteContext writeContext) throws WriteFailedException.CreateFailedException { try { - createSubInterface(id.firstKeyOf(Interface.class).getName(), dataAfter); + createSubInterface(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException e) { throw new WriteFailedException.CreateFailedException(id, dataAfter, e); } } - private void createSubInterface(final String swIfName, final SubInterface subInterface) throws VppApiInvocationException { + private void createSubInterface(final String swIfName, final SubInterface subInterface, + final WriteContext writeContext) throws VppApiInvocationException { final String superIfName = subInterface.getSuperInterface(); - final int swIfIndex = interfaceContext.getIndex(superIfName); + final int swIfIndex = interfaceContext.getIndex(superIfName, writeContext.getMappingContext()); LOG.debug("Creating sub interface of {}(id={}): name={}, subInterface={}", superIfName, swIfIndex, swIfName, subInterface); final CompletionStage<CreateSubifReply> createSubifReplyCompletionStage = getFutureJVpp().createSubif(getCreateSubifRequest(subInterface, swIfIndex)); @@ -96,7 +97,7 @@ public class SubInterfaceCustomizer extends AbstractInterfaceTypeCustomizer<SubI } else { LOG.debug("Sub interface created successfully for: {}, subInterface: {}", swIfName, subInterface); // Add new interface to our interface context - interfaceContext.addName(reply.swIfIndex, swIfName); + interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizer.java index 72679fe12..bafe788ad 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizer.java @@ -69,7 +69,7 @@ public class TapCustomizer extends AbstractInterfaceTypeCustomizer<Tap> { @Nonnull final WriteContext writeContext) throws WriteFailedException.CreateFailedException { try { - createTap(id.firstKeyOf(Interface.class).getName(), dataAfter); + createTap(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Write of Tap failed", e); throw new WriteFailedException.CreateFailedException(id, dataAfter, e); @@ -84,7 +84,7 @@ public class TapCustomizer extends AbstractInterfaceTypeCustomizer<Tap> { final int index; try { - index = interfaceContext.getIndex(ifcName); + index = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); } catch (IllegalArgumentException e) { throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); } @@ -105,20 +105,20 @@ public class TapCustomizer extends AbstractInterfaceTypeCustomizer<Tap> { final int index; try { - index = interfaceContext.getIndex(ifcName); + index = interfaceContext.getIndex(ifcName, writeContext.getMappingContext()); } catch (IllegalArgumentException e) { throw new WriteFailedException.DeleteFailedException(id, e); } try { - deleteTap(ifcName, index, dataBefore); + deleteTap(ifcName, index, dataBefore, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Delete of Tap failed", e); throw new WriteFailedException.DeleteFailedException(id, e); } } - private void createTap(final String swIfName, final Tap tap) throws VppApiInvocationException { + private void createTap(final String swIfName, final Tap tap, final WriteContext writeContext) throws VppApiInvocationException { LOG.debug("Setting tap interface: {}. Tap: {}", swIfName, tap); final CompletionStage<TapConnectReply> tapConnectFuture = getFutureJVpp().tapConnect(getTapConnectRequest(tap.getTapName(), tap.getMac(), tap.getDeviceInstance())); @@ -130,7 +130,7 @@ public class TapCustomizer extends AbstractInterfaceTypeCustomizer<Tap> { } else { LOG.debug("Tap set successfully for: {}, tap: {}", swIfName, tap); // Add new interface to our interface context - interfaceContext.addName(reply.swIfIndex, swIfName); + interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); } } @@ -148,7 +148,8 @@ public class TapCustomizer extends AbstractInterfaceTypeCustomizer<Tap> { } } - private void deleteTap(final String swIfName, final int index, final Tap dataBefore) + private void deleteTap(final String swIfName, final int index, final Tap dataBefore, + final WriteContext writeContext) throws VppApiInvocationException { LOG.debug("Deleting tap interface: {}. Tap: {}", swIfName, dataBefore); final CompletionStage<TapDeleteReply> vxlanAddDelTunnelReplyCompletionStage = @@ -161,7 +162,7 @@ public class TapCustomizer extends AbstractInterfaceTypeCustomizer<Tap> { } else { LOG.debug("Tap deleted successfully for: {}, tap: {}", swIfName, dataBefore); // Remove deleted interface from interface context - interfaceContext.removeName(swIfName); + interfaceContext.removeName(swIfName, writeContext.getMappingContext()); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java index ebefff34a..5f7d626e8 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java @@ -73,13 +73,13 @@ public class VhostUserCustomizer extends AbstractInterfaceTypeCustomizer<VhostUs @Nonnull final VhostUser dataAfter, @Nonnull final WriteContext writeContext) throws WriteFailedException.CreateFailedException { try { - createVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataAfter); + createVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException | IllegalInterfaceTypeException e) { throw new WriteFailedException.CreateFailedException(id, dataAfter, e); } } - private void createVhostUserIf(final String swIfName, final VhostUser vhostUser) throws VppApiInvocationException { + private void createVhostUserIf(final String swIfName, final VhostUser vhostUser, final WriteContext writeContext) throws VppApiInvocationException { LOG.debug("Creating vhost user interface: name={}, vhostUser={}", swIfName, vhostUser); final CompletionStage<CreateVhostUserIfReply> createVhostUserIfReplyCompletionStage = getFutureJVpp().createVhostUserIf(getCreateVhostUserIfRequest(vhostUser)); @@ -92,7 +92,7 @@ public class VhostUserCustomizer extends AbstractInterfaceTypeCustomizer<VhostUs } else { LOG.debug("Vhost user interface created successfully for: {}, vhostUser: {}", swIfName, vhostUser); // Add new interface to our interface context - interfaceContext.addName(reply.swIfIndex, swIfName); + interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); } } @@ -118,17 +118,17 @@ public class VhostUserCustomizer extends AbstractInterfaceTypeCustomizer<VhostUs } try { - modifyVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataAfter); + modifyVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException e) { throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); } } - private void modifyVhostUserIf(final String swIfName, final VhostUser vhostUser) throws VppApiInvocationException { + private void modifyVhostUserIf(final String swIfName, final VhostUser vhostUser, final WriteContext writeContext) throws VppApiInvocationException { LOG.debug("Updating vhost user interface: name={}, vhostUser={}", swIfName, vhostUser); final CompletionStage<ModifyVhostUserIfReply> modifyVhostUserIfReplyCompletionStage = getFutureJVpp() - .modifyVhostUserIf(getModifyVhostUserIfRequest(vhostUser, interfaceContext.getIndex(swIfName))); + .modifyVhostUserIf(getModifyVhostUserIfRequest(vhostUser, interfaceContext.getIndex(swIfName, writeContext.getMappingContext()))); final ModifyVhostUserIfReply reply = V3poUtils.getReply(modifyVhostUserIfReplyCompletionStage.toCompletableFuture()); @@ -155,16 +155,16 @@ public class VhostUserCustomizer extends AbstractInterfaceTypeCustomizer<VhostUs @Nonnull final VhostUser dataBefore, @Nonnull final WriteContext writeContext) throws WriteFailedException.DeleteFailedException { try { - deleteVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataBefore); + deleteVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataBefore, writeContext); } catch (VppApiInvocationException e) { throw new WriteFailedException.DeleteFailedException(id, e); } } - private void deleteVhostUserIf(final String swIfName, final VhostUser vhostUser) throws VppApiInvocationException { + private void deleteVhostUserIf(final String swIfName, final VhostUser vhostUser, final WriteContext writeContext) throws VppApiInvocationException { LOG.debug("Deleting vhost user interface: name={}, vhostUser={}", swIfName, vhostUser); final CompletionStage<DeleteVhostUserIfReply> deleteVhostUserIfReplyCompletionStage = - getFutureJVpp().deleteVhostUserIf(getDeleteVhostUserIfRequest(interfaceContext.getIndex(swIfName))); + getFutureJVpp().deleteVhostUserIf(getDeleteVhostUserIfRequest(interfaceContext.getIndex(swIfName, writeContext.getMappingContext()))); final DeleteVhostUserIfReply reply = V3poUtils.getReply(deleteVhostUserIfReplyCompletionStage.toCompletableFuture()); @@ -174,7 +174,7 @@ public class VhostUserCustomizer extends AbstractInterfaceTypeCustomizer<VhostUs } else { LOG.debug("Vhost user interface deleted successfully for: {}, vhostUser: {}", swIfName, vhostUser); // Remove interface from our interface context - interfaceContext.removeName(swIfName); + interfaceContext.removeName(swIfName, writeContext.getMappingContext()); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VlanTagRewriteCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VlanTagRewriteCustomizer.java index 7e7e319eb..d1e55a0a0 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VlanTagRewriteCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VlanTagRewriteCustomizer.java @@ -70,14 +70,15 @@ public class VlanTagRewriteCustomizer extends FutureJVppCustomizer implements Ch @Nonnull final VlanTagRewrite dataAfter, @Nonnull final WriteContext writeContext) throws WriteFailedException.CreateFailedException { try { - setTagRewrite(id.firstKeyOf(Interface.class).getName(), dataAfter); + setTagRewrite(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException e) { throw new WriteFailedException.CreateFailedException(id, dataAfter, e); } } - private void setTagRewrite(final String ifname, final VlanTagRewrite cfg) throws VppApiInvocationException { - final int swIfIndex = interfaceContext.getIndex(ifname); + private void setTagRewrite(final String ifname, final VlanTagRewrite cfg, final WriteContext writeContext) + throws VppApiInvocationException { + final int swIfIndex = interfaceContext.getIndex(ifname, writeContext.getMappingContext()); LOG.debug("Setting tag rewrite for interface {}(id=): {}", ifname, swIfIndex, cfg); final CompletionStage<L2InterfaceVlanTagRewriteReply> replyCompletionStage = @@ -121,7 +122,7 @@ public class VlanTagRewriteCustomizer extends FutureJVppCustomizer implements Ch return; } try { - setTagRewrite(id.firstKeyOf(Interface.class).getName(), dataAfter); + setTagRewrite(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException e) { throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); } @@ -135,7 +136,7 @@ public class VlanTagRewriteCustomizer extends FutureJVppCustomizer implements Ch // disable tag rewrite final VlanTagRewriteBuilder builder = new VlanTagRewriteBuilder(); builder.setRewriteOperation(TagRewriteOperation.Disabled); - setTagRewrite(id.firstKeyOf(Interface.class).getName(), builder.build()); + setTagRewrite(id.firstKeyOf(Interface.class).getName(), builder.build(), writeContext); } catch (VppApiInvocationException e) { throw new WriteFailedException.DeleteFailedException(id, e); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java index 9d11b59b1..3edd531c6 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java @@ -71,7 +71,7 @@ public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer<Vxlan> { @Nonnull final WriteContext writeContext) throws WriteFailedException.CreateFailedException { try { - createVxlanTunnel(id.firstKeyOf(Interface.class).getName(), dataAfter); + createVxlanTunnel(id.firstKeyOf(Interface.class).getName(), dataAfter, writeContext); } catch (VppApiInvocationException | IllegalInterfaceTypeException e) { LOG.warn("Write of Vxlan failed", e); throw new WriteFailedException.CreateFailedException(id, dataAfter, e); @@ -95,14 +95,14 @@ public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer<Vxlan> { @Nonnull final WriteContext writeContext) throws WriteFailedException.DeleteFailedException { try { - deleteVxlanTunnel(id.firstKeyOf(Interface.class).getName(), dataBefore); + deleteVxlanTunnel(id.firstKeyOf(Interface.class).getName(), dataBefore, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Delete of Vxlan tunnel failed", e); throw new WriteFailedException.DeleteFailedException(id, e); } } - private void createVxlanTunnel(final String swIfName, final Vxlan vxlan) throws VppApiInvocationException { + private void createVxlanTunnel(final String swIfName, final Vxlan vxlan, final WriteContext writeContext) throws VppApiInvocationException { final byte isIpv6 = (byte) (isIpv6(vxlan) ? 1 : 0); final InetAddress srcAddress = InetAddresses.forString(getAddressString(vxlan.getSrc())); final InetAddress dstAddress = InetAddresses.forString(getAddressString(vxlan.getDst())); @@ -123,7 +123,7 @@ public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer<Vxlan> { } else { LOG.debug("Vxlan tunnel set successfully for: {}, vxlan: {}", swIfName, vxlan); // Add new interface to our interface context - interfaceContext.addName(reply.swIfIndex, swIfName); + interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext()); } } @@ -143,7 +143,7 @@ public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer<Vxlan> { return addr.getIpv4Address() == null ? addr.getIpv6Address().getValue() : addr.getIpv4Address().getValue(); } - private void deleteVxlanTunnel(final String swIfName, final Vxlan vxlan) throws VppApiInvocationException { + private void deleteVxlanTunnel(final String swIfName, final Vxlan vxlan, final WriteContext writeContext) throws VppApiInvocationException { final byte isIpv6 = (byte) (isIpv6(vxlan) ? 1 : 0); final InetAddress srcAddress = InetAddresses.forString(getAddressString(vxlan.getSrc())); final InetAddress dstAddress = InetAddresses.forString(getAddressString(vxlan.getDst())); @@ -164,7 +164,7 @@ public class VxlanCustomizer extends AbstractInterfaceTypeCustomizer<Vxlan> { } else { LOG.debug("Vxlan tunnel deleted successfully for: {}, vxlan: {}", swIfName, vxlan); // Remove interface from our interface context - interfaceContext.removeName(swIfName); + interfaceContext.removeName(swIfName, writeContext.getMappingContext()); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/ip/Ipv4Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/ip/Ipv4Customizer.java index a54f6c5e3..4deb38f52 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/ip/Ipv4Customizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/ip/Ipv4Customizer.java @@ -68,7 +68,7 @@ public class Ipv4Customizer extends FutureJVppCustomizer implements ChildWriterC throws WriteFailedException { try { final String ifcName = id.firstKeyOf(Interface.class).getName(); - setIpv4(id, ifcName, dataAfter); + setIpv4(id, ifcName, dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Create of Ipv4 failed", e); throw new WriteFailedException.CreateFailedException(id, dataAfter, e); @@ -84,7 +84,7 @@ public class Ipv4Customizer extends FutureJVppCustomizer implements ChildWriterC // TODO handle update in a better way try { - setIpv4(id, ifcName, dataAfter); + setIpv4(id, ifcName, dataAfter, writeContext); } catch (VppApiInvocationException e) { LOG.warn("Update of Ipv4 failed", e); throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); @@ -97,9 +97,10 @@ public class Ipv4Customizer extends FutureJVppCustomizer implements ChildWriterC // TODO implement delete } - private void setIpv4(final InstanceIdentifier<Ipv4> id, final String name, final Ipv4 ipv4) + private void setIpv4(final InstanceIdentifier<Ipv4> id, final String name, final Ipv4 ipv4, + final WriteContext writeContext) throws WriteFailedException, VppApiInvocationException { - final int swIfc = interfaceContext.getIndex(name); + final int swIfc = interfaceContext.getIndex(name, writeContext.getMappingContext()); for (Address ipv4Addr : ipv4.getAddress()) { Subnet subnet = ipv4Addr.getSubnet(); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/EthernetCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/EthernetCustomizer.java index 74ea45c11..8173b67ba 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/EthernetCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/EthernetCustomizer.java @@ -16,7 +16,9 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; -import io.fd.honeycomb.v3po.translate.Context; +import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceCustomizer.getCachedInterfaceDump; + +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -64,11 +66,11 @@ public class EthernetCustomizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Ethernet> id, @Nonnull final EthernetBuilder builder, - @Nonnull final Context ctx) throws ReadFailedException { + @Nonnull final ReadContext ctx) throws ReadFailedException { final InterfaceKey key = id.firstKeyOf(Interface.class); final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key, - interfaceContext.getIndex(key.getName()), ctx); + interfaceContext.getIndex(key.getName(), ctx.getMappingContext()), ctx.getModificationCache()); builder.setMtu((int) iface.linkMtu); switch (iface.linkDuplex) { diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java index 51d4022d7..87c62f48f 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java @@ -16,7 +16,8 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.ModificationCache; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ListReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -69,13 +70,13 @@ public class InterfaceCustomizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull InstanceIdentifier<Interface> id, @Nonnull InterfaceBuilder builder, - @Nonnull Context ctx) throws ReadFailedException { + @Nonnull ReadContext ctx) throws ReadFailedException { LOG.debug("Reading attributes for interface: {}", id); final InterfaceKey key = id.firstKeyOf(id.getTargetType()); // Pass cached details from getAllIds to getDetails to avoid additional dumps final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key, - interfaceContext.getIndex(key.getName()), ctx); + interfaceContext.getIndex(key.getName(), ctx.getMappingContext()), ctx.getModificationCache()); LOG.debug("Interface details for interface: {}, details: {}", key.getName(), iface); builder.setName(key.getName()); @@ -94,7 +95,7 @@ public class InterfaceCustomizer extends FutureJVppCustomizer @Nonnull @SuppressWarnings("unchecked") - public static Map<Integer, SwInterfaceDetails> getCachedInterfaceDump(final @Nonnull Context ctx) { + public static Map<Integer, SwInterfaceDetails> getCachedInterfaceDump(final @Nonnull ModificationCache ctx) { return ctx.get(DUMPED_IFCS_CONTEXT_KEY) == null ? new HashMap<>() // allow customizers to update the cache : (Map<Integer, SwInterfaceDetails>) ctx.get(DUMPED_IFCS_CONTEXT_KEY); @@ -103,7 +104,7 @@ public class InterfaceCustomizer extends FutureJVppCustomizer @Nonnull @Override public List<InterfaceKey> getAllIds(@Nonnull final InstanceIdentifier<Interface> id, - @Nonnull final Context context) throws ReadFailedException { + @Nonnull final ReadContext context) throws ReadFailedException { LOG.trace("Dumping all interfaces to get all IDs"); final SwInterfaceDump request = new SwInterfaceDump(); @@ -120,20 +121,20 @@ public class InterfaceCustomizer extends FutureJVppCustomizer } // Cache interfaces dump in per-tx context to later be used in readCurrentAttributes - context.put(DUMPED_IFCS_CONTEXT_KEY, ifaces.swInterfaceDetails.stream() + context.getModificationCache().put(DUMPED_IFCS_CONTEXT_KEY, ifaces.swInterfaceDetails.stream() .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails))); final List<InterfaceKey> interfacesKeys = ifaces.swInterfaceDetails.stream() .filter(elt -> elt != null) .map((elt) -> { // Store interface name from VPP in context if not yet present - if (!interfaceContext.containsName(elt.swIfIndex)) { - interfaceContext.addName(elt.swIfIndex, V3poUtils.toString(elt.interfaceName)); + if (!interfaceContext.containsName(elt.swIfIndex, context.getMappingContext())) { + interfaceContext.addName(elt.swIfIndex, V3poUtils.toString(elt.interfaceName), context.getMappingContext()); } LOG.trace("Interface with name: {}, VPP name: {} and index: {} found in VPP", - interfaceContext.getName(elt.swIfIndex), elt.interfaceName, elt.swIfIndex); + interfaceContext.getName(elt.swIfIndex, context.getMappingContext()), elt.interfaceName, elt.swIfIndex); - return new InterfaceKey(interfaceContext.getName(elt.swIfIndex)); + return new InterfaceKey(interfaceContext.getName(elt.swIfIndex, context.getMappingContext())); }) .collect(Collectors.toList()); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java index a6c887336..ee66aef1f 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java @@ -21,7 +21,7 @@ import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceCusto import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; import java.math.BigInteger; import java.util.Map; @@ -158,7 +158,7 @@ public final class InterfaceUtils { @Nonnull public static SwInterfaceDetails getVppInterfaceDetails(@Nonnull final FutureJVpp futureJvpp, @Nonnull InterfaceKey key, final int index, - @Nonnull final Context ctx) { + @Nonnull final ModificationCache ctx) { final SwInterfaceDump request = new SwInterfaceDump(); request.nameFilter = key.getName().getBytes(); request.nameFilterValid = 1; @@ -225,7 +225,7 @@ public final class InterfaceUtils { return EthernetCsmacd.class; } - static boolean isInterfaceOfType(final Context ctx, final int index, + static boolean isInterfaceOfType(final ModificationCache ctx, final int index, final Class<? extends InterfaceType> ifcType) { final SwInterfaceDetails cachedDetails = checkNotNull(getCachedInterfaceDump(ctx).get(index), diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/L2Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/L2Customizer.java index 099d4c195..1bc9c2b3a 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/L2Customizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/L2Customizer.java @@ -17,7 +17,7 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; import com.google.common.base.Preconditions; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -74,20 +74,20 @@ public class L2Customizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull final InstanceIdentifier<L2> id, @Nonnull final L2Builder builder, - @Nonnull final Context ctx) throws ReadFailedException { + @Nonnull final ReadContext ctx) throws ReadFailedException { LOG.debug("Reading attributes for L2: {}", id); final InterfaceKey key = id.firstKeyOf(Interface.class); - final int ifaceId = interfaceContext.getIndex(key.getName()); + final int ifaceId = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key, - ifaceId, ctx); + ifaceId, ctx.getModificationCache()); LOG.debug("Interface details for interface: {}, details: {}", key.getName(), iface); final Optional<BridgeDomainSwIfDetails> bdForInterface = getBridgeDomainForInterface(ifaceId); if (bdForInterface.isPresent()) { final BridgeDomainSwIfDetails bdSwIfDetails = bdForInterface.get(); final BridgeBasedBuilder bbBuilder = new BridgeBasedBuilder(); - bbBuilder.setBridgeDomain(bridgeDomainContext.getName(bdSwIfDetails.bdId)); + bbBuilder.setBridgeDomain(bridgeDomainContext.getName(bdSwIfDetails.bdId, ctx.getMappingContext())); // bbBuilder.setBridgedVirtualInterface // TODO where to find that value? if (bdSwIfDetails.shg != 0) { bbBuilder.setSplitHorizonGroup((short)bdSwIfDetails.shg); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/SubInterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/SubInterfaceCustomizer.java index b68acbfa3..ebd1cff40 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/SubInterfaceCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/SubInterfaceCustomizer.java @@ -19,7 +19,7 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceUtils.isInterfaceOfType; import com.google.common.base.Preconditions; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -70,18 +70,18 @@ public class SubInterfaceCustomizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull final InstanceIdentifier<SubInterface> id, - @Nonnull final SubInterfaceBuilder builder, @Nonnull final Context ctx) + @Nonnull final SubInterfaceBuilder builder, @Nonnull final ReadContext ctx) throws ReadFailedException { final InterfaceKey key = id.firstKeyOf(Interface.class); // Relying here that parent InterfaceCustomizer was invoked first (PREORDER) // to fill in the context with initial ifc mapping - final int index = interfaceContext.getIndex(key.getName()); - if (!isInterfaceOfType(ctx, index, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.SubInterface.class)) { + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!isInterfaceOfType(ctx.getModificationCache(), index, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.SubInterface.class)) { return; } LOG.debug("Reading attributes for sub interface: {}", id); - final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key, index, ctx); + final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key, index, ctx.getModificationCache()); LOG.debug("VPP interface details: {}", ReflectionToStringBuilder.toString(iface)); if (iface.subId == 0) { @@ -90,7 +90,7 @@ public class SubInterfaceCustomizer extends FutureJVppCustomizer } builder.setIdentifier(Long.valueOf(iface.subId)); - builder.setSuperInterface(interfaceContext.getName(iface.supSwIfIndex)); + builder.setSuperInterface(interfaceContext.getName(iface.supSwIfIndex, ctx.getMappingContext())); builder.setNumberOfTags(Short.valueOf(iface.subNumberOfTags)); builder.setVlanType(iface.subDot1Ad == 1 ? VlanType._802dot1q : VlanType._802dot1ad); if (iface.subExactMatch == 1) { diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java index e7cd560bb..358e9149a 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java @@ -18,7 +18,7 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceUtils.isInterfaceOfType; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -72,12 +72,12 @@ public class TapCustomizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Tap> id, @Nonnull final TapBuilder builder, - @Nonnull final Context ctx) throws ReadFailedException { + @Nonnull final ReadContext ctx) throws ReadFailedException { final InterfaceKey key = id.firstKeyOf(Interface.class); // Relying here that parent InterfaceCustomizer was invoked first (PREORDER) // to fill in the context with initial ifc mapping - final int index = interfaceContext.getIndex(key.getName()); - if (!isInterfaceOfType(ctx, index, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Tap.class)) { + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!isInterfaceOfType(ctx.getModificationCache(), index, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Tap.class)) { return; } @@ -85,7 +85,7 @@ public class TapCustomizer extends FutureJVppCustomizer @SuppressWarnings("unchecked") Map<Integer, SwInterfaceTapDetails> mappedTaps = - (Map<Integer, SwInterfaceTapDetails>) ctx.get(DUMPED_TAPS_CONTEXT_KEY); + (Map<Integer, SwInterfaceTapDetails>) ctx.getModificationCache().get(DUMPED_TAPS_CONTEXT_KEY); if(mappedTaps == null) { // Full Tap dump has to be performed here, no filter or anything is here to help so at least we cache it @@ -104,7 +104,7 @@ public class TapCustomizer extends FutureJVppCustomizer .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails)); } - ctx.put(DUMPED_TAPS_CONTEXT_KEY, mappedTaps); + ctx.getModificationCache().put(DUMPED_TAPS_CONTEXT_KEY, mappedTaps); } final SwInterfaceTapDetails swInterfaceTapDetails = mappedTaps.get(index); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java index 70a6da1cc..d5ae8faf3 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java @@ -18,7 +18,7 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceUtils.isInterfaceOfType; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -74,12 +74,12 @@ public class VhostUserCustomizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VhostUser> id, @Nonnull final VhostUserBuilder builder, - @Nonnull final Context ctx) throws ReadFailedException { + @Nonnull final ReadContext ctx) throws ReadFailedException { final InterfaceKey key = id.firstKeyOf(Interface.class); // Relying here that parent InterfaceCustomizer was invoked first (PREORDER) // to fill in the context with initial ifc mapping - final int index = interfaceContext.getIndex(key.getName()); - if (!isInterfaceOfType(ctx, index, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUser.class)) { + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!isInterfaceOfType(ctx.getModificationCache(), index, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUser.class)) { return; } @@ -87,7 +87,7 @@ public class VhostUserCustomizer extends FutureJVppCustomizer @SuppressWarnings("unchecked") Map<Integer, SwInterfaceVhostUserDetails> mappedVhostUsers = - (Map<Integer, SwInterfaceVhostUserDetails>) ctx.get(DUMPED_VHOST_USERS_CONTEXT_KEY); + (Map<Integer, SwInterfaceVhostUserDetails>) ctx.getModificationCache().get(DUMPED_VHOST_USERS_CONTEXT_KEY); if(mappedVhostUsers == null) { // Full VhostUser dump has to be performed here, no filter or anything is here to help so at least we cache it @@ -106,7 +106,7 @@ public class VhostUserCustomizer extends FutureJVppCustomizer .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails)); } - ctx.put(DUMPED_VHOST_USERS_CONTEXT_KEY, mappedVhostUsers); + ctx.getModificationCache().put(DUMPED_VHOST_USERS_CONTEXT_KEY, mappedVhostUsers); } // Relying here that parent InterfaceCustomizer was invoked first to fill in the context with initial ifc mapping diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VlanTagRewriteCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VlanTagRewriteCustomizer.java index d7404e6d5..d05e089a2 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VlanTagRewriteCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VlanTagRewriteCustomizer.java @@ -17,7 +17,7 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; import com.google.common.base.Preconditions; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -68,13 +68,13 @@ public class VlanTagRewriteCustomizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VlanTagRewrite> id, - @Nonnull final VlanTagRewriteBuilder builder, @Nonnull final Context ctx) + @Nonnull final VlanTagRewriteBuilder builder, @Nonnull final ReadContext ctx) throws ReadFailedException { LOG.debug("Reading attributes for sub interface: {}", id); final InterfaceKey key = id.firstKeyOf(Interface.class); final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key, - interfaceContext.getIndex(key.getName()), ctx); + interfaceContext.getIndex(key.getName(), ctx.getMappingContext()), ctx.getModificationCache()); builder.setFirstPushed(iface.subDot1Ad == 1 ? VlanType._802dot1q : VlanType._802dot1ad); builder.setRewriteOperation(TagRewriteOperation.forValue(iface.vtrOp)); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VxlanCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VxlanCustomizer.java index 829416034..81ba34062 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VxlanCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VxlanCustomizer.java @@ -18,7 +18,7 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; import static com.google.common.base.Preconditions.checkState; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -74,12 +74,12 @@ public class VxlanCustomizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Vxlan> id, @Nonnull final VxlanBuilder builder, - @Nonnull final Context ctx) throws ReadFailedException { + @Nonnull final ReadContext ctx) throws ReadFailedException { final InterfaceKey key = id.firstKeyOf(Interface.class); // Relying here that parent InterfaceCustomizer was invoked first (PREORDER) // to fill in the context with initial ifc mapping - final int index = interfaceContext.getIndex(key.getName()); - if (!InterfaceUtils.isInterfaceOfType(ctx, index, VxlanTunnel.class)) { + final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext()); + if (!InterfaceUtils.isInterfaceOfType(ctx.getModificationCache(), index, VxlanTunnel.class)) { return; } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vpp/BridgeDomainCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vpp/BridgeDomainCustomizer.java index 672a05f4e..6e94461a6 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vpp/BridgeDomainCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vpp/BridgeDomainCustomizer.java @@ -96,11 +96,11 @@ public class BridgeDomainCustomizer // FIXME we need the bd index to be returned by VPP or we should have a counter field (maybe in context similar to artificial name) // Here we assign the next available ID from bdContext's perspective int index = 1; - while(bdContext.containsName(index)) { + while(bdContext.containsName(index, ctx.getMappingContext())) { index++; } addOrUpdateBridgeDomain(index, dataBefore); - bdContext.addName(index, bdName); + bdContext.addName(index, bdName, ctx.getMappingContext()); } catch (VppApiInvocationException e) { LOG.warn("Failed to create bridge domain", e); throw new WriteFailedException.CreateFailedException(id, dataBefore, e); @@ -120,7 +120,7 @@ public class BridgeDomainCustomizer LOG.debug("deleteCurrentAttributes: id={}, dataBefore={}, ctx={}", id, dataBefore, ctx); final String bdName = id.firstKeyOf(BridgeDomain.class).getName(); - int bdId = bdContext.getIndex(bdName); + int bdId = bdContext.getIndex(bdName, ctx.getMappingContext()); final BridgeDomainAddDel request = new BridgeDomainAddDel(); request.bdId = bdId; @@ -154,7 +154,7 @@ public class BridgeDomainCustomizer "BridgeDomain name changed. It should be deleted and then created."); try { - addOrUpdateBridgeDomain(bdContext.getIndex(bdName), dataAfter); + addOrUpdateBridgeDomain(bdContext.getIndex(bdName, ctx.getMappingContext()), dataAfter); } catch (VppApiInvocationException e) { LOG.warn("Failed to create bridge domain", e); throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizer.java index 1a3855ced..ac16c610f 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizer.java @@ -20,7 +20,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.primitives.Longs; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ListReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -70,7 +70,7 @@ public final class BridgeDomainCustomizer extends FutureJVppCustomizer @Override public void readCurrentAttributes(@Nonnull final InstanceIdentifier<BridgeDomain> id, - @Nonnull final BridgeDomainBuilder builder, @Nonnull final Context context) + @Nonnull final BridgeDomainBuilder builder, @Nonnull final ReadContext context) throws ReadFailedException { LOG.debug("vppstate.BridgeDomainCustomizer.readCurrentAttributes: id={}, builderbuilder={}, context={}", id, builder, context); @@ -78,13 +78,13 @@ public final class BridgeDomainCustomizer extends FutureJVppCustomizer final BridgeDomainKey key = id.firstKeyOf(id.getTargetType()); LOG.debug("vppstate.BridgeDomainCustomizer.readCurrentAttributes: key={}", key); - final int bdId = bdContext.getIndex(key.getName()); + final int bdId = bdContext.getIndex(key.getName(), context.getMappingContext()); LOG.debug("vppstate.BridgeDomainCustomizer.readCurrentAttributes: bdId={}", bdId); BridgeDomainDetailsReplyDump reply; BridgeDomainDetails bridgeDomainDetails; final BridgeDomainDump request = new BridgeDomainDump(); - request.bdId = bdContext.getIndex(key.getName()); + request.bdId = bdContext.getIndex(key.getName(), context.getMappingContext()); try { reply = getFutureJVpp().bridgeDomainDump(request).toCompletableFuture().get(); bridgeDomainDetails = Iterables.getOnlyElement(reply.bridgeDomainDetails); @@ -102,7 +102,7 @@ public final class BridgeDomainCustomizer extends FutureJVppCustomizer builder.setLearn(byteToBoolean(bridgeDomainDetails.learn)); builder.setUnknownUnicastFlood(byteToBoolean(bridgeDomainDetails.uuFlood)); - builder.setInterface(getIfcs(bridgeDomainDetails, reply.bridgeDomainSwIfDetails)); + builder.setInterface(getIfcs(bridgeDomainDetails, reply.bridgeDomainSwIfDetails, context)); final L2FibTableDump l2FibRequest = new L2FibTableDump(); l2FibRequest.bdId = bdId; @@ -124,7 +124,7 @@ public final class BridgeDomainCustomizer extends FutureJVppCustomizer ? L2Fib.Action.Filter : L2Fib.Action.Forward)) .setBridgedVirtualInterface(byteToBoolean(entry.bviMac)) - .setOutgoingInterface(interfaceContext.getName(entry.swIfIndex)) + .setOutgoingInterface(interfaceContext.getName(entry.swIfIndex, context.getMappingContext())) .setStaticConfig(byteToBoolean(entry.staticMac)) .setPhysAddress(address) .setKey(new L2FibKey(address)) @@ -175,10 +175,11 @@ public final class BridgeDomainCustomizer extends FutureJVppCustomizer } private List<Interface> getIfcs(final BridgeDomainDetails bridgeDomainDetails, - final List<BridgeDomainSwIfDetails> bridgeDomainSwIfDetails) { + final List<BridgeDomainSwIfDetails> bridgeDomainSwIfDetails, + final ReadContext context) { final List<Interface> ifcs = new ArrayList<>(bridgeDomainSwIfDetails.size()); for (BridgeDomainSwIfDetails anInterface : bridgeDomainSwIfDetails) { - final String interfaceName = interfaceContext.getName(anInterface.swIfIndex); + final String interfaceName = interfaceContext.getName(anInterface.swIfIndex, context.getMappingContext()); if (anInterface.bdId == bridgeDomainDetails.bdId) { ifcs.add(new InterfaceBuilder() .setBridgedVirtualInterface(bridgeDomainDetails.bviSwIfIndex == anInterface.swIfIndex) @@ -202,7 +203,7 @@ public final class BridgeDomainCustomizer extends FutureJVppCustomizer @Nonnull @Override public List<BridgeDomainKey> getAllIds(@Nonnull final InstanceIdentifier<BridgeDomain> id, - @Nonnull final Context context) { + @Nonnull final ReadContext context) { final BridgeDomainDump request = new BridgeDomainDump(); request.bdId = -1; // dump call @@ -228,7 +229,7 @@ public final class BridgeDomainCustomizer extends FutureJVppCustomizer for (BridgeDomainDetails detail : reply.bridgeDomainDetails) { logBridgeDomainDetails(detail); - final String bName = bdContext.getName(detail.bdId); + final String bName = bdContext.getName(detail.bdId, context.getMappingContext()); LOG.debug("vppstate.BridgeDomainCustomizer.getAllIds: bName={}", bName); allIds.add(new BridgeDomainKey(bName)); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/VersionCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/VersionCustomizer.java index 1db8217f9..4ad4a6e91 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/VersionCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/VersionCustomizer.java @@ -16,7 +16,7 @@ package io.fd.honeycomb.v3po.translate.v3po.vppstate; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; @@ -53,7 +53,7 @@ public final class VersionCustomizer @Override public void readCurrentAttributes(@Nonnull InstanceIdentifier<Version> id, @Nonnull final VersionBuilder builder, - @Nonnull final Context context) throws ReadFailedException { + @Nonnull final ReadContext context) throws ReadFailedException { ShowVersionReply reply; try { diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/ContextTestUtils.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/ContextTestUtils.java new file mode 100644 index 000000000..5c731bcb5 --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/ContextTestUtils.java @@ -0,0 +1,40 @@ +/* + * 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.translate.v3po; + +import com.google.common.base.Optional; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContextKey; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingBuilder; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +public class ContextTestUtils { + + public static Optional<Mapping> getMapping(final String name, final int index) { + return Optional.of(new MappingBuilder().setName(name).setIndex(index).build()); + } + + public static KeyedInstanceIdentifier<Mapping, MappingKey> getMappingIid(final String name, final String namingContextName) { + return InstanceIdentifier.create(Contexts.class).child( + org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext.class, + new NamingContextKey(namingContextName)).child(Mappings.class).child(Mapping.class, new MappingKey(name)); + } +} diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceTypeTestUtils.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceTypeTestUtils.java index ff96131b1..fbb47b5d4 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceTypeTestUtils.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/InterfaceTypeTestUtils.java @@ -19,21 +19,29 @@ package io.fd.honeycomb.v3po.translate.v3po.interfaces; import static org.mockito.Mockito.doReturn; import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.write.WriteContext; import org.mockito.Matchers; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContextKey; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingBuilder; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; final class InterfaceTypeTestUtils { private InterfaceTypeTestUtils() {} static void setupWriteContext(final WriteContext writeContext, final Class<? extends InterfaceType> ifcType) { - doReturn(new Context()).when(writeContext).getContext(); + doReturn(new ModificationCache()).when(writeContext).getModificationCache(); doReturn(Optional.of(new InterfaceBuilder() .setType(ifcType) .build())).when(writeContext).readAfter(Matchers.any(InstanceIdentifier.class)); } + } diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/SubInterfaceCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/SubInterfaceCustomizerTest.java index d70654655..31ab3aab5 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/SubInterfaceCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/SubInterfaceCustomizerTest.java @@ -16,15 +16,18 @@ package io.fd.honeycomb.v3po.translate.v3po.interfaces; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.MockitoAnnotations.initMocks; +import io.fd.honeycomb.v3po.translate.MappingContext; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import io.fd.honeycomb.v3po.translate.v3po.util.VppApiInvocationException; import io.fd.honeycomb.v3po.translate.write.WriteContext; @@ -54,6 +57,8 @@ public class SubInterfaceCustomizerTest { private FutureJVpp api; @Mock private WriteContext writeContext; + @Mock + private MappingContext mappingContext; private NamingContext namingContext; private SubInterfaceCustomizer customizer; @@ -65,10 +70,11 @@ public class SubInterfaceCustomizerTest { initMocks(this); InterfaceTypeTestUtils.setupWriteContext(writeContext, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.SubInterface.class); - namingContext = new NamingContext("generatedSubInterfaceName"); + namingContext = new NamingContext("generatedSubInterfaceName", "test-instance"); + doReturn(mappingContext).when(writeContext).getMappingContext(); // TODO create base class for tests using vppApi customizer = new SubInterfaceCustomizer(api, namingContext); - namingContext.addName(SUPER_IF_ID, SUPER_IF_NAME); + doReturn(getMapping(SUPER_IF_NAME, SUPER_IF_ID)).when(mappingContext).read(getMappingIid(SUPER_IF_NAME, "test-instance")); } private SubInterface generateSubInterface(final String superIfName) { @@ -145,7 +151,7 @@ public class SubInterfaceCustomizerTest { customizer.writeCurrentAttributes(id, subInterface, writeContext); verifyCreateSubifWasInvoked(generateSubInterfaceRequest(SUPER_IF_ID)); - assertTrue(namingContext.containsIndex(subIfaceName)); + verify(mappingContext).put(eq(getMappingIid(subIfaceName, "test-instance")), eq(getMapping(subIfaceName, 0).get())); } @Test @@ -161,7 +167,9 @@ public class SubInterfaceCustomizerTest { } catch (WriteFailedException.CreateFailedException e) { assertEquals(VppApiInvocationException.class, e.getCause().getClass()); verifyCreateSubifWasInvoked(generateSubInterfaceRequest(SUPER_IF_ID)); - assertFalse(namingContext.containsIndex(subIfaceName)); + verify(mappingContext, times(0)).put( + eq(getMappingIid(subIfaceName, "test-instance")), + eq(getMapping(subIfaceName, 0).get())); return; } fail("WriteFailedException.CreateFailedException was expected"); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizerTest.java index 8ae04f017..e717ec504 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/TapCustomizerTest.java @@ -16,14 +16,17 @@ package io.fd.honeycomb.v3po.translate.v3po.interfaces; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import io.fd.honeycomb.v3po.translate.write.WriteContext; import java.util.concurrent.CompletableFuture; @@ -55,8 +58,9 @@ public class TapCustomizerTest { private FutureJVpp vppApi; @Mock private WriteContext writeContext; + @Mock + private MappingContext mappingContext; - private NamingContext ctx; private TapCustomizer tapCustomizer; @Before @@ -64,7 +68,11 @@ public class TapCustomizerTest { MockitoAnnotations.initMocks(this); InterfaceTypeTestUtils.setupWriteContext(writeContext, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Tap.class); - ctx = new NamingContext("ifcintest"); + final NamingContext ctx = new NamingContext("ifcintest", "test-instance"); + final ModificationCache toBeReturned = new ModificationCache(); + doReturn(toBeReturned).when(writeContext).getModificationCache(); + doReturn(mappingContext).when(writeContext).getMappingContext(); + tapCustomizer = new TapCustomizer(vppApi, ctx); } @@ -89,8 +97,8 @@ public class TapCustomizerTest { tapCustomizer.writeCurrentAttributes(getTapId("tap2"), getTapData("tap2", "ff:ff:ff:ff:ff:ff"), writeContext); verify(vppApi, times(2)).tapConnect(any(TapConnect.class)); - assertTrue(ctx.containsIndex("tap")); - assertTrue(ctx.containsIndex("tap2")); + verify(mappingContext).put(eq(getMappingIid("tap", "test-instance")), eq(getMapping("tap", 0).get())); + verify(mappingContext).put(eq(getMappingIid("tap2", "test-instance")), eq(getMapping("tap2", 1).get())); } @Test @@ -109,12 +117,13 @@ public class TapCustomizerTest { doReturn(replyModif).when(vppApi).tapModify(any(TapModify.class)); tapCustomizer.writeCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), writeContext); + doReturn(getMapping("tap", 1)).when(mappingContext).read(getMappingIid("tap", "test-instance")); tapCustomizer.updateCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), getTapData("tap", "ff:ff:ff:ff:ff:f1"), writeContext); verify(vppApi).tapConnect(any(TapConnect.class)); verify(vppApi).tapModify(any(TapModify.class)); - assertTrue(ctx.containsIndex("tap")); - assertFalse(ctx.containsIndex("tap2")); + + verify(mappingContext).put(eq(getMappingIid("tap", "test-instance")), eq(getMapping("tap", 0).get())); } @Test @@ -132,11 +141,12 @@ public class TapCustomizerTest { doReturn(replyDelete).when(vppApi).tapDelete(any(TapDelete.class)); tapCustomizer.writeCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), writeContext); + doReturn(getMapping("tap", 1)).when(mappingContext).read(getMappingIid("tap", "test-instance")); tapCustomizer.deleteCurrentAttributes(getTapId("tap"), getTapData("tap", "ff:ff:ff:ff:ff:ff"), writeContext); verify(vppApi).tapConnect(any(TapConnect.class)); verify(vppApi).tapDelete(any(TapDelete.class)); - assertFalse(ctx.containsIndex("tap")); + verify(mappingContext).delete(eq(getMappingIid("tap", "test-instance"))); } private InstanceIdentifier<Tap> getTapId(final String tap) { diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java index 15b2a75c1..1015f24c0 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java @@ -16,19 +16,25 @@ package io.fd.honeycomb.v3po.translate.v3po.interfaces; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import io.fd.honeycomb.v3po.translate.v3po.util.VppApiInvocationException; import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; @@ -63,8 +69,9 @@ public class VhostUserCustomizerTest { private FutureJVpp api; @Mock private WriteContext writeContext; + @Mock + private MappingContext mappingContext; - private NamingContext namingContext; private VhostUserCustomizer customizer; private static final int IFACE_ID = 1; private static final String IFACE_NAME = "eth0"; @@ -77,7 +84,11 @@ public class VhostUserCustomizerTest { initMocks(this); InterfaceTypeTestUtils.setupWriteContext(writeContext, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUser.class); - namingContext = new NamingContext("generatedInterfaceName"); + final NamingContext namingContext = new NamingContext("generatedInterfaceName", "test-instance"); + final ModificationCache toBeReturned = new ModificationCache(); + doReturn(toBeReturned).when(writeContext).getModificationCache(); + doReturn(mappingContext).when(writeContext).getMappingContext(); + // TODO create base class for tests using vppApi customizer = new VhostUserCustomizer(api, namingContext); } @@ -186,7 +197,7 @@ public class VhostUserCustomizerTest { customizer.writeCurrentAttributes(ID, vhostUser, writeContext); verifyCreateVhostUserIfWasInvoked(vhostUser); - assertTrue(namingContext.containsIndex(IFACE_NAME)); + verify(mappingContext).put(eq(getMappingIid(IFACE_NAME, "test-instance")), eq(getMapping(IFACE_NAME, 0).get())); } @Test @@ -200,7 +211,7 @@ public class VhostUserCustomizerTest { } catch (WriteFailedException.CreateFailedException e) { assertEquals(VppApiInvocationException.class, e.getCause().getClass()); verifyCreateVhostUserIfWasInvoked(vhostUser); - assertFalse(namingContext.containsIndex(IFACE_NAME)); + verifyZeroInteractions(mappingContext); return; } fail("WriteFailedException.CreateFailedException was expected"); @@ -210,7 +221,7 @@ public class VhostUserCustomizerTest { public void testUpdateCurrentAttributes() throws Exception { final VhostUser vhostUserBefore = generateVhostUser(VhostUserRole.Client, "socketName0"); final VhostUser vhostUserAfter = generateVhostUser(VhostUserRole.Server, "socketName1"); - namingContext.addName(IFACE_ID, IFACE_NAME); + doReturn(getMapping(IFACE_NAME, IFACE_ID)).when(mappingContext).read(getMappingIid(IFACE_NAME, "test-instance")); whenModifyVhostUserIfThenSuccess(); @@ -230,7 +241,7 @@ public class VhostUserCustomizerTest { public void testUpdateCurrentAttributesFailed() throws Exception { final VhostUser vhostUserBefore = generateVhostUser(VhostUserRole.Client, "socketName0"); final VhostUser vhostUserAfter = generateVhostUser(VhostUserRole.Server, "socketName1"); - namingContext.addName(IFACE_ID, IFACE_NAME); + doReturn(getMapping(IFACE_NAME, IFACE_ID)).when(mappingContext).read(getMappingIid(IFACE_NAME, "test-instance")); whenModifyVhostUserIfThenFailure(); @@ -247,19 +258,19 @@ public class VhostUserCustomizerTest { @Test public void testDeleteCurrentAttributes() throws Exception { final VhostUser vhostUser = generateVhostUser(VhostUserRole.Client, "socketName"); - namingContext.addName(IFACE_ID, IFACE_NAME); + doReturn(getMapping(IFACE_NAME, IFACE_ID)).when(mappingContext).read(getMappingIid(IFACE_NAME, "test-instance")); whenDeleteVhostUserIfThenSuccess(); customizer.deleteCurrentAttributes(ID, vhostUser, writeContext); verifyDeleteVhostUserIfWasInvoked(IFACE_ID); - assertFalse(namingContext.containsIndex(IFACE_NAME)); + verify(mappingContext).delete(eq(getMappingIid(IFACE_NAME, "test-instance"))); } @Test public void testDeleteCurrentAttributesFailed() throws Exception { final VhostUser vhostUser = generateVhostUser(VhostUserRole.Client, "socketName"); - namingContext.addName(IFACE_ID, IFACE_NAME); + doReturn(getMapping(IFACE_NAME, IFACE_ID)).when(mappingContext).read(getMappingIid(IFACE_NAME, "test-instance")); whenDeleteVhostUserIfThenFailure(); @@ -268,7 +279,9 @@ public class VhostUserCustomizerTest { } catch (WriteFailedException.DeleteFailedException e) { assertEquals(VppApiInvocationException.class, e.getCause().getClass()); verifyDeleteVhostUserIfWasInvoked(IFACE_ID); - assertTrue(namingContext.containsIndex(IFACE_NAME)); + // Delete from context not invoked if delete from VPP failed + verify(mappingContext, times(0)).delete(eq(getMappingIid(IFACE_NAME, "test-instance"))); + verify(mappingContext).read(eq(getMappingIid(IFACE_NAME, "test-instance"))); return; } fail("WriteFailedException.DeleteFailedException was expected"); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VlanTagRewriteCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VlanTagRewriteCustomizerTest.java index ae768145a..39a9823b5 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VlanTagRewriteCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VlanTagRewriteCustomizerTest.java @@ -16,6 +16,8 @@ package io.fd.honeycomb.v3po.translate.v3po.interfaces; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; @@ -24,6 +26,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.MockitoAnnotations.initMocks; +import io.fd.honeycomb.v3po.translate.MappingContext; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import io.fd.honeycomb.v3po.translate.v3po.util.VppApiInvocationException; import io.fd.honeycomb.v3po.translate.write.WriteContext; @@ -55,6 +58,8 @@ public class VlanTagRewriteCustomizerTest { private FutureJVpp api; @Mock private WriteContext writeContext; + @Mock + private MappingContext mappingContext; private NamingContext namingContext; private VlanTagRewriteCustomizer customizer; @@ -65,9 +70,10 @@ public class VlanTagRewriteCustomizerTest { @Before public void setUp() throws Exception { initMocks(this); - namingContext = new NamingContext("generatedSubInterfaceName"); + namingContext = new NamingContext("generatedSubInterfaceName", "test-instance"); + doReturn(mappingContext).when(writeContext).getMappingContext(); customizer = new VlanTagRewriteCustomizer(api, namingContext); - namingContext.addName(VLAN_IF_ID, VLAN_IF_NAME); + doReturn(getMapping(VLAN_IF_NAME, VLAN_IF_ID)).when(mappingContext).read(getMappingIid(VLAN_IF_NAME, "test-instance")); } private InstanceIdentifier<VlanTagRewrite> getVlanTagRewriteId(final String name) { diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizerTest.java index af7b4df17..a5103b178 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizerTest.java @@ -16,19 +16,24 @@ package io.fd.honeycomb.v3po.translate.v3po.interfaces; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.net.InetAddresses; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import io.fd.honeycomb.v3po.translate.v3po.util.VppApiInvocationException; import io.fd.honeycomb.v3po.translate.write.WriteContext; @@ -63,9 +68,10 @@ public class VxlanCustomizerTest { private FutureJVpp api; @Mock private WriteContext writeContext; + @Mock + private MappingContext mappingContext; private VxlanCustomizer customizer; - private NamingContext namingContext; private String ifaceName; private InstanceIdentifier<Vxlan> id; @@ -75,7 +81,11 @@ public class VxlanCustomizerTest { InterfaceTypeTestUtils.setupWriteContext(writeContext, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel.class); // TODO create base class for tests using vppApi - namingContext = new NamingContext("generateInterfaceNAme"); + NamingContext namingContext = new NamingContext("generateInterfaceNAme", "test-instance"); + final ModificationCache toBeReturned = new ModificationCache(); + doReturn(toBeReturned).when(writeContext).getModificationCache(); + doReturn(mappingContext).when(writeContext).getMappingContext(); + customizer = new VxlanCustomizer(api, namingContext); ifaceName = "eth0"; @@ -144,7 +154,7 @@ public class VxlanCustomizerTest { customizer.writeCurrentAttributes(id, vxlan, writeContext); verifyVxlanAddWasInvoked(vxlan); - assertTrue(namingContext.containsIndex(ifaceName)); + verify(mappingContext).put(eq(getMappingIid(ifaceName, "test-instance")), eq(getMapping(ifaceName, 0).get())); } @Test @@ -158,7 +168,8 @@ public class VxlanCustomizerTest { } catch (WriteFailedException.CreateFailedException e) { assertEquals(VppApiInvocationException.class, e.getCause().getClass()); verifyVxlanAddWasInvoked(vxlan); - assertFalse(namingContext.containsIndex(ifaceName)); + // Mapping not stored due to failure + verify(mappingContext, times(0)).put(eq(getMappingIid(ifaceName, "test-instance")), eq(getMapping(ifaceName, 0).get())); return; } fail("WriteFailedException.CreateFailedException was expected"); @@ -186,11 +197,11 @@ public class VxlanCustomizerTest { final Vxlan vxlan = generateVxlan(); whenVxlanAddDelTunnelThenSuccess(); - namingContext.addName(1, ifaceName); + doReturn(getMapping(ifaceName, 1)).when(mappingContext).read(getMappingIid(ifaceName, "test-instance")); customizer.deleteCurrentAttributes(id, vxlan, writeContext); verifyVxlanDeleteWasInvoked(vxlan); - assertFalse(namingContext.containsIndex(ifaceName)); + verify(mappingContext).delete(eq(getMappingIid(ifaceName, "test-instance"))); } @Test @@ -198,14 +209,14 @@ public class VxlanCustomizerTest { final Vxlan vxlan = generateVxlan(); whenVxlanAddDelTunnelThenFailure(); - namingContext.addName(1, ifaceName); + doReturn(getMapping(ifaceName, 1)).when(mappingContext).read(getMappingIid(ifaceName, "test-instance")); try { customizer.deleteCurrentAttributes(id, vxlan, writeContext); } catch (WriteFailedException.DeleteFailedException e) { assertEquals(VppApiInvocationException.class, e.getCause().getClass()); verifyVxlanDeleteWasInvoked(vxlan); - assertTrue(namingContext.containsIndex(ifaceName)); + verify(mappingContext, times(0)).delete(eq(getMappingIid(ifaceName, "test-instance"))); return; } fail("WriteFailedException.DeleteFailedException was expected"); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizerTest.java index 3f33d141f..822b16c2b 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizerTest.java @@ -16,16 +16,21 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceUtils.yangIfIndexToVpp; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.test.ListReaderCustomizerTest; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; @@ -37,12 +42,17 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.MappingsBuilder; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesStateBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.openvpp.jvpp.dto.SwInterfaceDetails; import org.openvpp.jvpp.dto.SwInterfaceDetailsReplyDump; import org.openvpp.jvpp.dto.SwInterfaceDump; @@ -58,13 +68,23 @@ public class InterfaceCustomizerTest extends @Override public void setUpBefore() { - interfacesContext = new NamingContext("generatedIfaceName"); + interfacesContext = new NamingContext("generatedIfaceName", "test-instance"); } @Override protected RootReaderCustomizer<Interface, InterfaceBuilder> initCustomizer() { - interfacesContext.addName(0, "eth0"); - interfacesContext.addName(1, "eth1"); + final KeyedInstanceIdentifier<Mapping, MappingKey> eth0Id = getMappingIid("eth0", "test-instance"); + final KeyedInstanceIdentifier<Mapping, MappingKey> eth1Id = getMappingIid("eth1", "test-instance"); + final Optional<Mapping> eth0 = getMapping("eth0", 0); + final Optional<Mapping> eth1 = getMapping("eth1", 1); + + final List<Mapping> allMappings = Lists.newArrayList(getMapping("eth0", 0).get(), getMapping("eth1", 1).get()); + final Mappings allMappingsBaObject = new MappingsBuilder().setMapping(allMappings).build(); + doReturn(Optional.of(allMappingsBaObject)).when(mappingContext).read(eth0Id.firstIdentifierOf(Mappings.class)); + + doReturn(eth0).when(mappingContext).read(eth0Id); + doReturn(eth1).when(mappingContext).read(eth1Id); + return new InterfaceCustomizer(api, interfacesContext); } diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/L2CustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/L2CustomizerTest.java index b454446dc..b09381584 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/L2CustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/L2CustomizerTest.java @@ -16,12 +16,16 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import io.fd.honeycomb.v3po.translate.Context; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.test.ChildReaderCustomizerTest; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; @@ -33,6 +37,10 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.MappingsBuilder; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; @@ -43,6 +51,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.l2.Interconnection; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.l2.interconnection.BridgeBasedBuilder; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.openvpp.jvpp.dto.BridgeDomainDetailsReplyDump; import org.openvpp.jvpp.dto.BridgeDomainDump; import org.openvpp.jvpp.dto.BridgeDomainSwIfDetails; @@ -59,8 +68,8 @@ public class L2CustomizerTest extends ChildReaderCustomizerTest<L2, L2Builder> { @Override public void setUpBefore() { - interfaceContext = new NamingContext("generatedIfaceName"); - bridgeDomainContext = new NamingContext("generatedBDName"); + interfaceContext = new NamingContext("generatedIfaceName", "ifc-test-instance"); + bridgeDomainContext = new NamingContext("generatedBDName", "bd-test-instance"); } @Override @@ -111,19 +120,28 @@ public class L2CustomizerTest extends ChildReaderCustomizerTest<L2, L2Builder> { @Test public void testRead() throws Exception { - final Context ctx = new Context(); final Map<Integer, SwInterfaceDetails> cachedInterfaceDump = new HashMap<>(); final int ifId = 1; final int bdId = 1; final String bdName = "bd001"; final String ifName = "eth0.sub0"; - interfaceContext.addName(ifId, ifName); - bridgeDomainContext.addName(bdId, bdName); + final KeyedInstanceIdentifier<Mapping, MappingKey> ifcIid = getMappingIid(ifName, "ifc-test-instance"); + doReturn(getMapping(ifName, ifId)).when(mappingContext).read(ifcIid); + final KeyedInstanceIdentifier<Mapping, MappingKey> bdIid = getMappingIid(bdName, "bd-test-instance"); + doReturn(getMapping(bdName, bdId)).when(mappingContext).read(bdIid); + + List<Mapping> allMappings = Lists.newArrayList(getMapping(ifName, ifId).get()); + Mappings allMappingsBaObject = new MappingsBuilder().setMapping(allMappings).build(); + doReturn(Optional.of(allMappingsBaObject)).when(mappingContext).read(ifcIid.firstIdentifierOf(Mappings.class)); + + allMappings = Lists.newArrayList(getMapping(bdName, bdId).get()); + allMappingsBaObject = new MappingsBuilder().setMapping(allMappings).build(); + doReturn(Optional.of(allMappingsBaObject)).when(mappingContext).read(bdIid.firstIdentifierOf(Mappings.class)); final SwInterfaceDetails ifaceDetails = new SwInterfaceDetails(); ifaceDetails.subId = ifId; cachedInterfaceDump.put(ifId, ifaceDetails); - ctx.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, cachedInterfaceDump); + cache.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, cachedInterfaceDump); whenBridgeDomainSwIfDumpThenReturn(Collections.singletonList(generateBdSwIfDetails(ifId, bdId))); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/SubInterfaceCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/SubInterfaceCustomizerTest.java index 8ed35e01b..4f6857b47 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/SubInterfaceCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/SubInterfaceCustomizerTest.java @@ -16,19 +16,29 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import io.fd.honeycomb.v3po.translate.Context; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.test.ChildReaderCustomizerTest; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.MappingsBuilder; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; @@ -38,6 +48,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.SubInterface; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.SubInterfaceBuilder; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.openvpp.jvpp.dto.SwInterfaceDetails; public class SubInterfaceCustomizerTest extends ChildReaderCustomizerTest<SubInterface, SubInterfaceBuilder> { @@ -55,7 +66,7 @@ public class SubInterfaceCustomizerTest extends ChildReaderCustomizerTest<SubInt @Override public void setUpBefore() { - interfacesContext = new NamingContext("generatedIfaceName"); + interfacesContext = new NamingContext("generatedIfaceName", "test-instance"); } private InstanceIdentifier<SubInterface> getSubInterfaceId(final String name) { @@ -74,21 +85,30 @@ public class SubInterfaceCustomizerTest extends ChildReaderCustomizerTest<SubInt @Test public void testRead() throws ReadFailedException { - final Context ctx = new Context(); final Map<Integer, SwInterfaceDetails> cachedInterfaceDump = new HashMap<>(); final int ifId = 1; final String ifName = "eth0.sub0"; - interfacesContext.addName(ifId, ifName); + + final KeyedInstanceIdentifier<Mapping, MappingKey> ifcIid = getMappingIid(ifName, "test-instance"); + doReturn(getMapping(ifName, ifId)).when(mappingContext).read(ifcIid); + final KeyedInstanceIdentifier<Mapping, MappingKey> superIfcIid = getMappingIid("super", "test-instance"); + doReturn(getMapping("super", 0)).when(mappingContext).read(superIfcIid); + + final List<Mapping> allMappings = Lists.newArrayList(getMapping(ifName, ifId).get(), getMapping("super", 0).get()); + final Mappings allMappingsBaObject = new MappingsBuilder().setMapping(allMappings).build(); + doReturn(Optional.of(allMappingsBaObject)).when(mappingContext).read(ifcIid.firstIdentifierOf(Mappings.class)); + final SwInterfaceDetails ifaceDetails = new SwInterfaceDetails(); ifaceDetails.subId = ifId; ifaceDetails.interfaceName = ifName.getBytes(); cachedInterfaceDump.put(ifId, ifaceDetails); - ctx.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, cachedInterfaceDump); + cache.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, cachedInterfaceDump); final SubInterfaceBuilder builder = mock(SubInterfaceBuilder.class); getCustomizer().readCurrentAttributes(getSubInterfaceId(ifName), builder, ctx); verify(builder).setIdentifier((long)ifId); + verify(builder).setSuperInterface(anyString()); verify(builder).setNumberOfTags((short)0); verify(builder).setVlanType(VlanType._802dot1ad); verify(builder, never()).setExactMatch(any()); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VlanTagRewriteCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VlanTagRewriteCustomizerTest.java index 2263160e3..d00745ec1 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VlanTagRewriteCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VlanTagRewriteCustomizerTest.java @@ -16,12 +16,14 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import io.fd.honeycomb.v3po.translate.Context; import io.fd.honeycomb.v3po.translate.read.ReadFailedException; import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.test.ChildReaderCustomizerTest; @@ -52,7 +54,7 @@ public class VlanTagRewriteCustomizerTest extends ChildReaderCustomizerTest<Vlan @Override public void setUpBefore() { - interfacesContext = new NamingContext("generatedIfaceName"); + interfacesContext = new NamingContext("generatedIfaceName", "test-instance"); } @@ -76,15 +78,15 @@ public class VlanTagRewriteCustomizerTest extends ChildReaderCustomizerTest<Vlan @Test public void testRead() throws ReadFailedException { - final Context ctx = new Context(); final Map<Integer, SwInterfaceDetails> cachedInterfaceDump = new HashMap<>(); final int ifId = 1; final String ifName = "eth0.sub0"; - interfacesContext.addName(ifId, ifName); + doReturn(getMapping(ifName, ifId)).when(mappingContext).read(getMappingIid(ifName, "test-instance")); + final SwInterfaceDetails ifaceDetails = new SwInterfaceDetails(); ifaceDetails.subId = ifId; cachedInterfaceDump.put(ifId, ifaceDetails); - ctx.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, cachedInterfaceDump); + cache.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, cachedInterfaceDump); final VlanTagRewriteBuilder builder = mock(VlanTagRewriteBuilder.class); getCustomizer().readCurrentAttributes(getVlanTagRewriteId(ifName), builder, ctx); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VxlanCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VxlanCustomizerTest.java index 760e85b26..db92ebf17 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VxlanCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VxlanCustomizerTest.java @@ -16,6 +16,8 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -25,7 +27,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import com.google.common.collect.Lists; -import io.fd.honeycomb.v3po.translate.Context; import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.test.ChildReaderCustomizerTest; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; @@ -50,7 +51,6 @@ import org.openvpp.jvpp.dto.VxlanTunnelDump; public class VxlanCustomizerTest extends ChildReaderCustomizerTest<Vxlan, VxlanBuilder> { private NamingContext interfacesContext; - private Context ctx; static final InstanceIdentifier<Vxlan> IID = InstanceIdentifier.create(InterfacesState.class).child(Interface.class, new InterfaceKey("ifc1")) .augmentation(VppInterfaceStateAugmentation.class).child(Vxlan.class); @@ -61,15 +61,14 @@ public class VxlanCustomizerTest extends ChildReaderCustomizerTest<Vxlan, VxlanB @Override public void setUpBefore() { - interfacesContext = new NamingContext("vxlan-tunnel"); - interfacesContext.addName(0, "ifc1"); + interfacesContext = new NamingContext("vxlan-tunnel", "test-instance"); + doReturn(getMapping("ifc1", 0)).when(mappingContext).read(getMappingIid("ifc1", "test-instance")); - ctx = new Context(); final SwInterfaceDetails v = new SwInterfaceDetails(); v.interfaceName = "vxlan-tunnel4".getBytes(); final Map<Integer, SwInterfaceDetails> map = new HashMap<>(); map.put(0, v); - ctx.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, map); + cache.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, map); } @Override @@ -114,7 +113,7 @@ public class VxlanCustomizerTest extends ChildReaderCustomizerTest<Vxlan, VxlanB @Test(expected = NullPointerException.class) public void testReadCurrentAttributesVppNameNotCached() throws Exception { - InterfaceCustomizer.getCachedInterfaceDump(ctx).remove(0); + InterfaceCustomizer.getCachedInterfaceDump(cache).remove(0); final VxlanBuilder builder = getCustomizer().getBuilder(IID); getCustomizer().readCurrentAttributes(IID, builder, ctx); @@ -124,7 +123,7 @@ public class VxlanCustomizerTest extends ChildReaderCustomizerTest<Vxlan, VxlanB public void testReadCurrentAttributesWrongType() throws Exception { final SwInterfaceDetails v = new SwInterfaceDetails(); v.interfaceName = "tap-2".getBytes(); - InterfaceCustomizer.getCachedInterfaceDump(ctx).put(0, v); + InterfaceCustomizer.getCachedInterfaceDump(cache).put(0, v); final VxlanBuilder builder = getCustomizer().getBuilder(IID); getCustomizer().readCurrentAttributes(IID, builder, ctx); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/test/RootReaderCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/test/RootReaderCustomizerTest.java index 19d307ec9..8500acece 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/test/RootReaderCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/test/RootReaderCustomizerTest.java @@ -17,9 +17,12 @@ package io.fd.honeycomb.v3po.translate.v3po.test; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.doReturn; import static org.mockito.MockitoAnnotations.initMocks; -import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; +import io.fd.honeycomb.v3po.translate.read.ReadContext; import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer; import org.junit.Before; import org.junit.Test; @@ -39,8 +42,11 @@ public abstract class RootReaderCustomizerTest<D extends DataObject, B extends B @Mock protected FutureJVpp api; + protected ModificationCache cache; @Mock - protected Context ctx; + protected ReadContext ctx; + @Mock + protected MappingContext mappingContext; protected final Class<D> dataObjectClass; private RootReaderCustomizer<D, B> customizer; @@ -52,6 +58,10 @@ public abstract class RootReaderCustomizerTest<D extends DataObject, B extends B @Before public void setUpParent() throws Exception { initMocks(this); + cache = new ModificationCache(); + doReturn(cache).when(ctx).getModificationCache(); + doReturn(mappingContext).when(ctx).getMappingContext(); + setUpBefore(); customizer = initCustomizer(); setUpAfter(); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/BridgeDomainCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/BridgeDomainCustomizerTest.java index f5b19a7be..7436c9cf3 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/BridgeDomainCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/BridgeDomainCustomizerTest.java @@ -15,15 +15,21 @@ */ package io.fd.honeycomb.v3po.translate.v3po.vpp; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; 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 io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; import io.fd.honeycomb.v3po.translate.write.WriteContext; import io.fd.honeycomb.v3po.translate.write.WriteFailedException; @@ -34,6 +40,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; 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.openvpp.jvpp.dto.BridgeDomainAddDel; @@ -47,18 +54,22 @@ public class BridgeDomainCustomizerTest { @Mock private FutureJVpp api; - @Mock private WriteContext ctx; + @Mock + private MappingContext mappingContext; private BridgeDomainCustomizer customizer; - private NamingContext namingContext; @Before public void setUp() throws Exception { initMocks(this); // TODO create base class for tests using vppApi - namingContext = new NamingContext("generatedBDName"); + NamingContext namingContext = new NamingContext("generatedBDName", "test-instance"); + final ModificationCache toBeReturned = new ModificationCache(); + doReturn(toBeReturned).when(ctx).getModificationCache(); + doReturn(mappingContext).when(ctx).getMappingContext(); + customizer = new BridgeDomainCustomizer(api, namingContext); } @@ -139,12 +150,14 @@ public class BridgeDomainCustomizerTest { final int bdId = 1; final String bdName = "bd1"; final BridgeDomain bd = generateBridgeDomain(bdName); + doReturn(Optional.absent()).when(mappingContext).read(getMappingIid(bdName, "test-instance").firstIdentifierOf(Mappings.class)); whenBridgeDomainAddDelThenSuccess(); customizer.writeCurrentAttributes(BridgeDomainTestUtils.bdIdentifierForName(bdName), bd, ctx); verifyBridgeDomainAddOrUpdateWasInvoked(bd, bdId); + verify(mappingContext).put(getMappingIid(bdName, "test-instance"), getMapping(bdName, bdId).get()); } @Test @@ -153,6 +166,9 @@ public class BridgeDomainCustomizerTest { final String bdName = "bd1"; final BridgeDomain bd = generateBridgeDomain(bdName); + // Returning no Mappings for "test-instance" makes bdContext.containsName() return false + doReturn(Optional.absent()).when(mappingContext).read(getMappingIid(bdName, "test-instance").firstIdentifierOf(Mappings.class)); + whenBridgeDomainAddDelThenFailure(); try { @@ -169,7 +185,7 @@ public class BridgeDomainCustomizerTest { final int bdId = 1; final String bdName = "bd1"; final BridgeDomain bd = generateBridgeDomain(bdName); - namingContext.addName(bdId, bdName); + doReturn(getMapping(bdName, bdId)).when(mappingContext).read(getMappingIid(bdName, "test-instance")); whenBridgeDomainAddDelThenSuccess(); @@ -182,6 +198,7 @@ public class BridgeDomainCustomizerTest { public void testDeleteUnknownBridgeDomain() throws Exception { final String bdName = "bd1"; final BridgeDomain bd = generateBridgeDomain("bd1"); + doReturn(Optional.absent()).when(mappingContext).read(getMappingIid(bdName, "test-instance")); try { customizer.deleteCurrentAttributes(BridgeDomainTestUtils.bdIdentifierForName(bdName), bd, ctx); @@ -197,7 +214,7 @@ public class BridgeDomainCustomizerTest { final int bdId = 1; final String bdName = "bd1"; final BridgeDomain bd = generateBridgeDomain(bdName); - namingContext.addName(bdId, bdName); + doReturn(getMapping(bdName, bdId)).when(mappingContext).read(getMappingIid(bdName, "test-instance")); whenBridgeDomainAddDelThenFailure(); @@ -215,7 +232,7 @@ public class BridgeDomainCustomizerTest { public void testUpdateBridgeDomain() throws Exception { final int bdId = 1; final String bdName = "bd1"; - namingContext.addName(bdId, bdName); + doReturn(getMapping(bdName, bdId)).when(mappingContext).read(getMappingIid(bdName, "test-instance")); final byte arpTermBefore = 1; final byte floodBefore = 1; @@ -241,6 +258,7 @@ public class BridgeDomainCustomizerTest { final String bdName = "bd1"; final BridgeDomain bdBefore = generateBridgeDomain(bdName, 0, 1, 0 ,1, 0); final BridgeDomain bdAfter = generateBridgeDomain(bdName, 1, 1, 0 ,1, 0); + doReturn(Optional.absent()).when(mappingContext).read(getMappingIid(bdName, "test-instance")); try { customizer.updateCurrentAttributes(BridgeDomainTestUtils.bdIdentifierForName(bdName), bdBefore, bdAfter, ctx); @@ -257,7 +275,7 @@ public class BridgeDomainCustomizerTest { final String bdName = "bd1"; final BridgeDomain bdBefore = generateBridgeDomain(bdName, 0, 1, 0 ,1, 0); final BridgeDomain bdAfter = generateBridgeDomain(bdName, 1, 1, 0 ,1, 0); - namingContext.addName(bdId, bdName); + doReturn(getMapping(bdName, bdId)).when(mappingContext).read(getMappingIid(bdName, "test-instance")); whenBridgeDomainAddDelThenFailure(); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppTest.java index 820cf0555..1b30fdde7 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vpp/VppTest.java @@ -16,15 +16,21 @@ package io.fd.honeycomb.v3po.translate.v3po.vpp; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import com.google.common.base.Optional; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.impl.write.CompositeRootWriter; import io.fd.honeycomb.v3po.translate.util.write.DelegatingWriterRegistry; import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; @@ -38,6 +44,9 @@ import java.util.concurrent.ExecutionException; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; 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; @@ -54,17 +63,24 @@ public class VppTest { private static final byte ADD_OR_UPDATE_BD = 1; private static final byte ZERO = 0; + @Mock private FutureJVpp api; + @Mock + private WriteContext ctx; + @Mock + private MappingContext mappingContext; + private DelegatingWriterRegistry rootRegistry; private CompositeRootWriter<Vpp> vppWriter; - private WriteContext ctx; - private NamingContext bdContext; @Before public void setUp() throws Exception { - api = mock(FutureJVpp.class); - ctx = mock(WriteContext.class); - bdContext = new NamingContext("generatedBdName"); + MockitoAnnotations.initMocks(this); + NamingContext bdContext = new NamingContext("generatedBdName", "test-instance"); + final ModificationCache toBeReturned = new ModificationCache(); + doReturn(toBeReturned).when(ctx).getModificationCache(); + doReturn(mappingContext).when(ctx).getMappingContext(); + vppWriter = VppUtils.getVppWriter(api, bdContext); rootRegistry = new DelegatingWriterRegistry( Collections.<Writer<? extends DataObject>>singletonList(vppWriter)); @@ -134,6 +150,8 @@ public class VppTest { final int bdId = 1; final BridgeDomains bdn1 = getBridgeDomains("bdn1"); whenBridgeDomainAddDelThen(0); + doReturn(Optional + .absent()).when(mappingContext).read(getMappingIid("bdn1", "test-instance").firstIdentifierOf(Mappings.class)); rootRegistry.update( InstanceIdentifier.create(Vpp.class), @@ -149,6 +167,8 @@ public class VppTest { final int bdId = 1; final BridgeDomains bdn1 = getBridgeDomains("bdn1"); whenBridgeDomainAddDelThen(0); + doReturn(Optional + .absent()).when(mappingContext).read(getMappingIid("bdn1", "test-instance").firstIdentifierOf(Mappings.class)); vppWriter.update(InstanceIdentifier.create(Vpp.class), null, @@ -156,6 +176,7 @@ public class VppTest { ctx); verifyBridgeDomainAddDel(Iterators.getOnlyElement(bdn1.getBridgeDomain().iterator()), bdId); + verify(mappingContext).put(getMappingIid("bdn1", "test-instance"), getMapping("bdn1", 1).get()); } @Test @@ -163,9 +184,11 @@ public class VppTest { final BridgeDomains bdn1 = getBridgeDomains("bdn1"); final int bdId = 1; final Vpp vpp = new VppBuilder().setBridgeDomains(bdn1).build(); + doReturn(Optional + .absent()).when(mappingContext).read(getMappingIid("bdn1", "test-instance").firstIdentifierOf(Mappings.class)); whenBridgeDomainAddDelThen(0); - rootRegistry.update(Collections.<InstanceIdentifier<?>, DataObject>emptyMap(), + rootRegistry.update(Collections.emptyMap(), Collections.<InstanceIdentifier<?>, DataObject>singletonMap(InstanceIdentifier.create(Vpp.class), vpp), ctx); @@ -178,7 +201,7 @@ public class VppTest { final BridgeDomains bdn1 = getBridgeDomains(bdName); final int bdId = 1; whenBridgeDomainAddDelThen(0); - bdContext.addName(bdId, bdName); + doReturn(getMapping(bdName, bdId)).when(mappingContext).read(getMappingIid(bdName, "test-instance")); rootRegistry.update( InstanceIdentifier.create(Vpp.class), @@ -204,7 +227,8 @@ public class VppTest { public void writeUpdate() throws Exception { final String bdName = "bdn1"; final int bdn1Id = 1; - bdContext.addName(bdn1Id, bdName); + doReturn(getMapping(bdName, bdn1Id)).when(mappingContext).read(getMappingIid(bdName, "test-instance")); + final BridgeDomains domainsBefore = getBridgeDomains(bdName); final BridgeDomain bdn1Before = domainsBefore.getBridgeDomain().get(0); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizerTest.java index c482c4485..369038687 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/BridgeDomainCustomizerTest.java @@ -40,8 +40,8 @@ public class BridgeDomainCustomizerTest extends ListReaderCustomizerTest<BridgeD @Override public void setUpBefore() { - bdContext = new NamingContext("generatedBdName"); - interfacesContext = new NamingContext("generatedIfaceName"); + bdContext = new NamingContext("generatedBdName", "bd-test-instance"); + interfacesContext = new NamingContext("generatedIfaceName", "ifc-test-instance"); } @Test diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/VppStateTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/VppStateTest.java index bf1d12152..3d75d09e2 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/VppStateTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppstate/VppStateTest.java @@ -16,18 +16,24 @@ package io.fd.honeycomb.v3po.translate.v3po.vppstate; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.ContextTestUtils.getMappingIid; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.base.Optional; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.ModificationCache; import io.fd.honeycomb.v3po.translate.impl.read.CompositeListReader; import io.fd.honeycomb.v3po.translate.impl.read.CompositeRootReader; import io.fd.honeycomb.v3po.translate.read.ReadContext; @@ -43,6 +49,9 @@ import java.util.concurrent.ExecutionException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.MappingsBuilder; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppStateBuilder; @@ -72,6 +81,8 @@ public class VppStateTest { private FutureJVpp api; @Mock private ReadContext ctx; + @Mock + private MappingContext mappingContext; private NamingContext bdContext; private NamingContext interfaceContext; @@ -82,9 +93,12 @@ public class VppStateTest { @Before public void setUp() throws Exception { initMocks(this); + final ModificationCache cache = new ModificationCache(); + doReturn(cache).when(ctx).getModificationCache(); + doReturn(mappingContext).when(ctx).getMappingContext(); - bdContext = new NamingContext("generatedBdName"); - interfaceContext = new NamingContext("generatedInterfaceName"); + bdContext = new NamingContext("generatedBdName", "bd-test-instance"); + interfaceContext = new NamingContext("generatedIfaceName", "ifc-test-instance"); vppStateReader = VppStateTestUtils.getVppStateReader(api, bdContext, interfaceContext); readerRegistry = new DelegatingReaderRegistry(Collections.<Reader<? extends DataObject>>singletonList(vppStateReader)); } @@ -146,7 +160,14 @@ public class VppStateTest { final Version version = getVersion(); whenShowVersionThenReturn(0, version); - final List<BridgeDomainDetails> bdList = Arrays.asList(new BridgeDomainDetails(), new BridgeDomainDetails()); + final BridgeDomainDetails bridgeDomainDetails = new BridgeDomainDetails(); + final BridgeDomainDetails bridgeDomainDetails2 = new BridgeDomainDetails(); + bridgeDomainDetails2.bdId = 1; + + final List<BridgeDomainDetails> bdList = Arrays.asList(bridgeDomainDetails, bridgeDomainDetails2); + mockBdMapping(bridgeDomainDetails, "bd1"); + mockBdMapping(bridgeDomainDetails2, "bd2"); + whenBridgeDomainDumpThenReturn(bdList); final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects = readerRegistry.readAll(ctx); @@ -171,8 +192,10 @@ public class VppStateTest { public void testReadBridgeDomains() throws Exception { final Version version = getVersion(); whenShowVersionThenReturn(0, version); - whenBridgeDomainDumpThenReturn(Collections.singletonList(new BridgeDomainDetails())); + final BridgeDomainDetails details = new BridgeDomainDetails(); + whenBridgeDomainDumpThenReturn(Collections.singletonList(details)); + mockBdMapping(details, "bdn1"); VppState readRoot = (VppState) readerRegistry.read(InstanceIdentifier.create(VppState.class), ctx).get(); Optional<? extends DataObject> read = @@ -189,7 +212,9 @@ public class VppStateTest { final BridgeDomainDetails bd = new BridgeDomainDetails(); bd.bdId = 0; final String bdName = "bdn1"; - bdContext.addName(bd.bdId, bdName); + mockBdMapping(bd, bdName); + mockMapping("eth1", 0, "ifc-test-instance"); + whenBridgeDomainDumpThenReturn(Collections.singletonList(bd)); final L2FibTableEntry l2FibEntry = new L2FibTableEntry(); l2FibEntry.bdId = 0; @@ -204,18 +229,44 @@ public class VppStateTest { assertTrue(read.isPresent()); // non existing l2fib - read = - readerRegistry.read(InstanceIdentifier.create(VppState.class).child(BridgeDomains.class).child( + read = readerRegistry.read(InstanceIdentifier.create(VppState.class).child(BridgeDomains.class).child( BridgeDomain.class, new BridgeDomainKey("bdn1")) .child(L2Fib.class, new L2FibKey(new PhysAddress("FF:FF:FF:04:05:06"))), ctx); assertFalse(read.isPresent()); } + private void mockBdMapping(final BridgeDomainDetails bd, final String bdName) { + mockMapping(bdName, bd.bdId, "bd-test-instance"); + } + + private void mockMapping(final String name, final int id, final String namingContextName) { + final InstanceIdentifier<Mappings> mappingsIid = getMappingIid(name, namingContextName).firstIdentifierOf(Mappings.class); + + final Optional<Mapping> singleMapping = getMapping(name, id); + final Optional<Mappings> previousMappings = mappingContext.read(mappingsIid); + + final MappingsBuilder mappingsBuilder; + if(previousMappings != null && previousMappings.isPresent()) { + mappingsBuilder = new MappingsBuilder(previousMappings.get()); + } else { + mappingsBuilder = new MappingsBuilder(); + mappingsBuilder.setMapping(Lists.newArrayList()); + } + + final List<Mapping> mappingList = mappingsBuilder.getMapping(); + mappingList.add(singleMapping.get()); + doReturn(Optional.of(mappingsBuilder.setMapping(mappingList).build())) + .when(mappingContext).read(mappingsIid); + doReturn(singleMapping).when(mappingContext).read(getMappingIid(name, namingContextName)); + } + @Test public void testReadBridgeDomainAll() throws Exception { final Version version = getVersion(); whenShowVersionThenReturn(0, version); - whenBridgeDomainDumpThenReturn(Collections.singletonList(new BridgeDomainDetails())); + final BridgeDomainDetails details = new BridgeDomainDetails(); + whenBridgeDomainDumpThenReturn(Collections.singletonList(details)); + mockBdMapping(details, "bd2"); VppState readRoot = (VppState) readerRegistry.read(InstanceIdentifier.create(VppState.class), ctx).get(); @@ -234,7 +285,8 @@ public class VppStateTest { final BridgeDomainDetails bd = new BridgeDomainDetails(); bd.bdId = 0; final String bdName = "bdn1"; - bdContext.addName(bd.bdId, bdName); + mockBdMapping(bd, bdName); + whenBridgeDomainDumpThenReturn(Collections.singletonList(bd)); whenShowVersionThenReturn(0, getVersion()); @@ -245,12 +297,15 @@ public class VppStateTest { BridgeDomain.class, new BridgeDomainKey(bdName)), ctx); assertTrue(read.isPresent()); - assertEquals(Iterables.find(readRoot.getBridgeDomains().getBridgeDomain(), - input -> input.getKey().getName().equals(bdName)), read.get()); + assertEquals(readRoot.getBridgeDomains().getBridgeDomain().stream().filter( + input -> input.getKey().getName().equals(bdName)).findFirst().get(), + read.get()); } @Test(expected = IllegalArgumentException.class) public void testReadBridgeDomainNotExisting() throws Exception { + doReturn(Optional.absent()).when(mappingContext).read(getMappingIid("NOT EXISTING", "bd-test-instance")); + final Optional<? extends DataObject> read = readerRegistry.read(InstanceIdentifier.create(VppState.class).child(BridgeDomains.class).child( BridgeDomain.class, new BridgeDomainKey("NOT EXISTING")), ctx); diff --git a/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/AbstractInterfaceTypeCustomizer.java b/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/AbstractInterfaceTypeCustomizer.java index 45ac193cf..e1a5bf2bc 100644 --- a/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/AbstractInterfaceTypeCustomizer.java +++ b/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/AbstractInterfaceTypeCustomizer.java @@ -45,17 +45,17 @@ public abstract class AbstractInterfaceTypeCustomizer<D extends DataObject> } private void checkProperInterfaceType(@Nonnull final WriteContext writeContext, - @Nonnull final InstanceIdentifier<D> id) { + @Nonnull final InstanceIdentifier<D> id) { final InstanceIdentifier<Interface> ifcTypeFromIid = id.firstIdentifierOf(Interface.class); checkArgument(ifcTypeFromIid != null, "Instance identifier does not contain {} type", Interface.class); checkArgument(id.firstKeyOf(Interface.class) != null, "Instance identifier does not contain keyed {} type", Interface.class); - final Optional<DataObject> interfaceConfigOperational = writeContext.readAfter(ifcTypeFromIid); - checkState(interfaceConfigOperational.isPresent(), - "Unable to get Interface configuration for an interface being updated under ID"); + final Optional<Interface> interfaceConfig = writeContext.readAfter(ifcTypeFromIid); + checkState(interfaceConfig.isPresent(), + "Unable to get Interface configuration for an interface: %s currently being updated", ifcTypeFromIid); IllegalInterfaceTypeException - .checkInterfaceType((Interface) interfaceConfigOperational.get(), getExpectedInterfaceType()); + .checkInterfaceType(interfaceConfig.get(), getExpectedInterfaceType()); } protected abstract Class<? extends InterfaceType> getExpectedInterfaceType(); diff --git a/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/DataTreeNamingContext.java b/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/DataTreeNamingContext.java deleted file mode 100644 index 3b20b2876..000000000 --- a/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/DataTreeNamingContext.java +++ /dev/null @@ -1,193 +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.translate.v3po.util; - -import com.google.common.collect.Lists; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts; -import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; -import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; -import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Naming context keeping a mapping between int index and string name. - * Provides artificial names to unknown indices. - */ -@ThreadSafe -public class DataTreeNamingContext extends NamingContext { - - // FIXME this has to be accessed by readers/writers in a transactional manner and the transaction will only be committed once - // the Read or Write operation finishes successfully - // Context datatree has to become a first class citizen of Honeycomb, and Honeycomb needs to create and provide - // context read write transaction as part of context to read/write customizers - // This will then become just a utility writer relying on transaction provided by the infrastructure - // Btw. the context transaction needs to disable commit/submit when being passed to customizers - - private static final Logger LOG = LoggerFactory.getLogger(DataTreeNamingContext.class); - - private static final QName NAME_KEY_QNAME = QName.create(Contexts.QNAME, "name"); - private static final QName INDEX_QNAME = QName.create(Contexts.QNAME, "index"); - - private final String instanceName; - private final DataTree contextDataTree; - - private final YangInstanceIdentifier.NodeIdentifierWithPredicates namingContextNodeId; - private final YangInstanceIdentifier namingContextIid; - - public DataTreeNamingContext(final String artificialNamePrefix, final String instanceName, - final DataTree contextDataTree) { - super(artificialNamePrefix); - this.instanceName = instanceName; - this.contextDataTree = contextDataTree; - - namingContextNodeId = getNodeId( - org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext.QNAME, - Collections.singletonMap(NAME_KEY_QNAME, instanceName)); - namingContextIid = YangInstanceIdentifier.create( - getNodeId(Contexts.QNAME), - getNodeId(org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext.QNAME), - namingContextNodeId); - - // FIXME read current mappings and initialize map - mergeNewContextInDataTree(instanceName); - } - - // TODO move the data tree aspect into a dedicated class - private void mergeNewContextInDataTree(final String instanceName) { - final DataTreeModification dataTreeModification = getModification(); - - final YangInstanceIdentifier.NodeIdentifier namingContextsNodeIdForMapNode = getNodeId( - org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext.QNAME); - - final ContainerNode newMapping = Builders.containerBuilder() - .withNodeIdentifier(getNodeId(Contexts.QNAME)) - .withChild(Builders.mapBuilder() - .withNodeIdentifier(namingContextsNodeIdForMapNode) - .withChild(Builders.mapEntryBuilder() - .withNodeIdentifier(namingContextNodeId) - .withChild(ImmutableNodes.leafNode(NAME_KEY_QNAME, instanceName)) - .withChild(Builders.containerBuilder() - .withNodeIdentifier(getNodeId(Mappings.QNAME)) - .withChild(Builders.mapBuilder() - .withNodeIdentifier(getNodeId(Mapping.QNAME)) - .build()) - .build()) - .build()) - .build()) - .build(); - - // FIXME add logs or debug to resolve: -// 2016-05-13 15:48:52,401 | WARN | config-pusher | DataTreeNamingContext | 240 - io.fd.honeycomb.v3po.vpp-translate-utils - 1.0.0.SNAPSHOT | Unable to update context: interface-context in context data tree -// org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException: Node /(urn:honeycomb:params:xml:ns:yang:naming:context?revision=2016-05-13)contexts/naming-context/naming-context[{(urn:honeycomb:params:xml:ns:yang:naming:context?revision=2016-05-13)name=interface-context}] does not exist. Cannot apply modification to its children. -// at org.opendaylight.yangtools.yang.data.impl.schema.tree.AbstractNodeContainerModificationStrategy.checkTouchApplicable(AbstractNodeContainerModificationStrategy.java:276)[55:org.opendaylight.yangtools.yang-data-impl:0.8.0.Beryllium] - // FIXME looks like a timing issue, did not occur when debugging - - dataTreeModification.merge(YangInstanceIdentifier.create(getNodeId(Contexts.QNAME)), newMapping); - - commitModification(dataTreeModification); - } - - private void commitModification(final DataTreeModification dataTreeModification) { - try { - dataTreeModification.ready(); - contextDataTree.validate(dataTreeModification); - contextDataTree.commit(contextDataTree.prepare(dataTreeModification)); - } catch (DataValidationFailedException e) { - LOG.warn("Unable to update context: {} in context data tree", instanceName, e); - throw new IllegalStateException("Unable to update context in context data tree", e); - } - } - - private DataTreeModification getModification() { - final DataTreeSnapshot dataTreeSnapshot = contextDataTree.takeSnapshot(); - return dataTreeSnapshot.newModification(); - } - - private static YangInstanceIdentifier.NodeIdentifierWithPredicates getNodeId(@Nonnull final QName qName, - @Nonnull final Map<QName, Object> keys) { - return new YangInstanceIdentifier.NodeIdentifierWithPredicates(qName, keys); - } - - private static YangInstanceIdentifier.NodeIdentifier getNodeId(@Nonnull final QName qName) { - return new YangInstanceIdentifier.NodeIdentifier(qName); - } - - public synchronized void addName(final int index, final String name) { - addMappingToDataTree(name, index); - super.addName(index, name); - } - - private void addMappingToDataTree(final String name, final int index) { - final DataTreeModification dataTreeModification = getModification(); - - final YangInstanceIdentifier.NodeIdentifierWithPredicates mappingNodeId = getNodeId(Mapping.QNAME, - Collections.singletonMap(NAME_KEY_QNAME, name)); - - final List<YangInstanceIdentifier.PathArgument> pathArguments = namingContextIid.getPathArguments(); - final ArrayList<YangInstanceIdentifier.PathArgument> newPathArgs = Lists.newArrayList(pathArguments); - newPathArgs.add(getNodeId(Mappings.QNAME)); - newPathArgs.add(getNodeId(Mapping.QNAME)); - newPathArgs.add(mappingNodeId); - - final YangInstanceIdentifier identifier = YangInstanceIdentifier.create(newPathArgs); - - final NormalizedNode<?, ?> newMapping = Builders.mapEntryBuilder() - .withNodeIdentifier(mappingNodeId) - .withChild(ImmutableNodes.leafNode(NAME_KEY_QNAME, name)) - .withChild(ImmutableNodes.leafNode(INDEX_QNAME, index)) - .build(); - - dataTreeModification.write(identifier, newMapping); - - commitModification(dataTreeModification); - } - - public synchronized int removeName(@Nonnull final String name) { - removeMappingFromDataTree(name); - return super.removeName(name); - } - - private void removeMappingFromDataTree(final String name) { - final DataTreeModification dataTreeModification = getModification(); - - final YangInstanceIdentifier.NodeIdentifierWithPredicates mappingNodeId = getNodeId(Mapping.QNAME, - Collections.singletonMap(NAME_KEY_QNAME, name)); - - final YangInstanceIdentifier identifier = YangInstanceIdentifier.create( - namingContextIid.getLastPathArgument(), getNodeId(Mappings.QNAME), getNodeId(Mapping.QNAME), mappingNodeId); - - dataTreeModification.delete(identifier); - - commitModification(dataTreeModification); - } -} diff --git a/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java b/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java index 9affd0695..d32f27203 100644 --- a/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java +++ b/v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java @@ -17,48 +17,88 @@ package io.fd.honeycomb.v3po.translate.v3po.util; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import com.google.common.base.Optional; +import io.fd.honeycomb.v3po.translate.MappingContext; +import java.util.stream.Collector; +import java.util.stream.Collectors; import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContextKey; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingBuilder; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Naming context keeping a mapping between int index and string name. - * Provides artificial names to unknown indices. + * Utility adapter on top of {@link MappingContext} */ -public class NamingContext implements AutoCloseable { +public final class NamingContext implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(NamingContext.class); - private final BiMap<String, Integer> nameMapping = HashBiMap.create(); private final String artificialNamePrefix; + private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext, NamingContextKey> + namingContextIid; - public NamingContext(final String artificialNamePrefix) { + /** + * Collector expecting only a single resulting item from a stream + */ + private static final Collector<Mapping, ?, Mapping> SINGLE_ITEM_COLLECTOR = Collectors.collectingAndThen( + Collectors.toList(), + list -> { + if (list.size() != 1) { + throw new IllegalStateException("Unexpected size of list: " + list + ". Single item expected"); + } + return list.get(0); + }); + + public NamingContext(final String artificialNamePrefix, final String instanceName) { this.artificialNamePrefix = artificialNamePrefix; + namingContextIid = InstanceIdentifier.create(Contexts.class).child( + org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext.class, + new NamingContextKey(instanceName)); } @Nonnull - public synchronized String getName(final int index) { - if(!nameMapping.inverse().containsKey(index)) { + public synchronized String getName(final int index, final MappingContext mappingContext) { + if (!containsName(index, mappingContext)) { final String artificialName = getArtificialName(index); LOG.info("Assigning artificial name: {} for index: {}", artificialName, index); - addName(index, artificialName); + addName(index, artificialName, mappingContext); } - return nameMapping.inverse().get(index); + + final Optional<Mappings> read = mappingContext.read(namingContextIid.child(Mappings.class)); + checkState(read.isPresent(), "Mapping for index: %s is not present. But should be", index); + + return read.get().getMapping().stream() + .filter(mapping -> mapping.getIndex().equals(index)) + .collect(SINGLE_ITEM_COLLECTOR).getName(); + } + + public synchronized boolean containsName(final int index, final MappingContext mappingContext) { + final Optional<Mappings> read = mappingContext.read(namingContextIid.child(Mappings.class)); + return read.isPresent() + ? read.get().getMapping().stream().anyMatch(mapping -> mapping.getIndex().equals(index)) + : false; } - public synchronized boolean containsName(final int index) { - return nameMapping.inverse().containsKey(index); + public synchronized void addName(final int index, final String name, final MappingContext mappingContext) { + final KeyedInstanceIdentifier<Mapping, MappingKey> mappingIid = getMappingIid(name); + mappingContext.put(mappingIid, new MappingBuilder().setIndex(index).setName(name).build()); } - public synchronized void addName(final int index, final String name) { - nameMapping.put(name, index); + private KeyedInstanceIdentifier<Mapping, MappingKey> getMappingIid(final String name) { + return namingContextIid.child(Mappings.class).child(Mapping.class, new MappingKey(name)); } - public synchronized int removeName(final String name) { - return nameMapping.remove(name); + public synchronized void removeName(final String name, final MappingContext mappingContext) { + mappingContext.delete(getMappingIid(name)); } /** @@ -68,14 +108,15 @@ public class NamingContext implements AutoCloseable { * @return integer index value matching supplied name * @throws IllegalArgumentException if name was not found */ - public synchronized int getIndex(String name) { - checkArgument(nameMapping.containsKey(name), "Name %s not found. Known names: %s", - name, nameMapping); - return nameMapping.get(name); + public synchronized int getIndex(final String name, final MappingContext mappingContext) { + final Optional<Mapping> read = mappingContext.read(getMappingIid(name)); + checkArgument(read.isPresent(), "No mapping stored for name: %s", name); + return read.get().getIndex(); + } - public synchronized boolean containsIndex(String interfaceName) { - return nameMapping.containsKey(interfaceName); + public synchronized boolean containsIndex(final String name, final MappingContext mappingContext) { + return mappingContext.read(getMappingIid(name)).isPresent(); } private String getArtificialName(final int index) { @@ -84,6 +125,6 @@ public class NamingContext implements AutoCloseable { @Override public void close() throws Exception { - nameMapping.clear(); + /// Not removing the mapping from backing storage } } diff --git a/v3po/vpp-translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/util/rev160406/NamingContextImplModule.java b/v3po/vpp-translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/util/rev160406/NamingContextImplModule.java index 50b577a25..60a816d89 100644 --- a/v3po/vpp-translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/util/rev160406/NamingContextImplModule.java +++ b/v3po/vpp-translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/util/rev160406/NamingContextImplModule.java @@ -1,6 +1,6 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.util.rev160406; -import io.fd.honeycomb.v3po.translate.v3po.util.DataTreeNamingContext; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; public class NamingContextImplModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.util.rev160406.AbstractNamingContextImplModule { public NamingContextImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { @@ -18,10 +18,9 @@ public class NamingContextImplModule extends org.opendaylight.yang.gen.v1.urn.ho @Override public java.lang.AutoCloseable createInstance() { - return new DataTreeNamingContext( + return new NamingContext( getArtificialNamePrefix(), - getIdentifier().getInstanceName(), - getContextDataTreeDependency()); + getIdentifier().getInstanceName()); } } diff --git a/v3po/vpp-translate-utils/src/main/yang/vpp-util.yang b/v3po/vpp-translate-utils/src/main/yang/vpp-util.yang index bd903848c..23615d09e 100644 --- a/v3po/vpp-translate-utils/src/main/yang/vpp-util.yang +++ b/v3po/vpp-translate-utils/src/main/yang/vpp-util.yang @@ -31,15 +31,6 @@ module vpp-util { leaf artificial-name-prefix { type string; } - - container context-data-tree { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dapi:data-tree; - } - } - } } } |