f636e6708c92327b7708bd7bbf3263c904e07947
[honeycomb.git] / v3po / data-impl / src / main / java / io / fd / honeycomb / v3po / data / impl / ConfigDataTree.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.honeycomb.v3po.data.impl;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static io.fd.honeycomb.v3po.data.impl.DataTreeUtils.childrenFromNormalized;
21
22 import com.google.common.base.Optional;
23 import com.google.common.util.concurrent.CheckedFuture;
24 import com.google.common.util.concurrent.Futures;
25 import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
26 import io.fd.honeycomb.v3po.data.ModifiableDataTree;
27 import io.fd.honeycomb.v3po.data.ReadableDataTree;
28 import io.fd.honeycomb.v3po.translate.TranslationException;
29 import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext;
30 import io.fd.honeycomb.v3po.translate.write.WriteContext;
31 import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
32 import java.util.Collections;
33 import java.util.Map;
34 import javax.annotation.Nonnull;
35 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
37 import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
44 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
45 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
47 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 /**
52  * DataTree implementation for configuration data.
53  */
54 public final class ConfigDataTree implements ModifiableDataTree {
55
56     private static final Logger LOG = LoggerFactory.getLogger(ConfigDataTree.class);
57
58     private final BindingNormalizedNodeSerializer serializer;
59     private final DataTree dataTree;
60     private final WriterRegistry writer;
61     public static final ReadableDataTree EMPTY_OPERATIONAL = new ReadableDataTree() {
62         @Override
63         public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
64                 @Nonnull final YangInstanceIdentifier path) {
65             return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>absent());
66         }
67     };
68
69     /**
70      * Creates configuration data tree instance.
71      *
72      * @param serializer service for serialization between Java Binding Data representation and NormalizedNode
73      *                   representation.
74      * @param dataTree   data tree for configuration data representation
75      * @param writer  service for translation between Java Binding Data and data provider.
76      */
77     public ConfigDataTree(@Nonnull final BindingNormalizedNodeSerializer serializer,
78                           @Nonnull final DataTree dataTree, @Nonnull final WriterRegistry writer) {
79         this.serializer = checkNotNull(serializer, "serializer should not be null");
80         this.dataTree = checkNotNull(dataTree, "dataTree should not be null");
81         this.writer = checkNotNull(writer, "writer should not be null");
82     }
83
84     @Override
85     public DataTreeSnapshot takeSnapshot() {
86         return new ConfigSnapshot(dataTree.takeSnapshot());
87     }
88
89     @Override
90     public void modify(final DataTreeModification modification)
91             throws DataValidationFailedException, TranslationException {
92         dataTree.validate(modification);
93
94         final DataTreeCandidate candidate = dataTree.prepare(modification);
95
96         final DataTreeCandidateNode rootNode = candidate.getRootNode();
97         final YangInstanceIdentifier rootPath = candidate.getRootPath();
98         final Optional<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore();
99         final Optional<NormalizedNode<?, ?>> normalizedDataAfter = rootNode.getDataAfter();
100         LOG.debug("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}",
101                 rootPath, rootNode, normalizedDataBefore, normalizedDataAfter);
102
103         final Map<InstanceIdentifier<?>, DataObject> nodesBefore = extractNetconfData(normalizedDataBefore);
104         LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet());
105
106         final Map<InstanceIdentifier<?>, DataObject> nodesAfter = extractNetconfData(normalizedDataAfter);
107         LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet());
108
109
110         final DOMDataReadOnlyTransaction beforeTx = new ReadOnlyTransaction(EMPTY_OPERATIONAL, takeSnapshot());
111         final ConfigSnapshot modificationSnapshot = new ConfigSnapshot(modification);
112         final DOMDataReadOnlyTransaction afterTx = new ReadOnlyTransaction(EMPTY_OPERATIONAL, modificationSnapshot);
113         try(final WriteContext ctx = new TransactionWriteContext(serializer, beforeTx, afterTx)) {
114             writer.update(nodesBefore, nodesAfter, ctx);
115         } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) {
116             LOG.warn("Failed to apply all changes", e);
117             LOG.info("Trying to revert successful changes for current transaction");
118
119             try {
120                 e.revertChanges();
121                 LOG.info("Changes successfully reverted");
122             } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException) {
123                 // fail with failed revert
124                 LOG.error("Failed to revert successful changes", revertFailedException);
125                 throw revertFailedException;
126             }
127
128             throw e; // fail with success revert
129         } catch (TranslationException e) {
130             LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e);
131             throw e;
132         }
133
134         dataTree.commit(candidate);
135     }
136
137     private Map<InstanceIdentifier<?>, DataObject> extractNetconfData(
138             final Optional<NormalizedNode<?, ?>> parentOptional) {
139         if (parentOptional.isPresent()) {
140             final DataContainerNode parent = (DataContainerNode) parentOptional.get();
141             return childrenFromNormalized(parent, serializer);
142         }
143         return Collections.emptyMap();
144     }
145
146     private final static class ConfigSnapshot implements DataTreeSnapshot {
147         private final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot;
148
149         ConfigSnapshot(@Nonnull final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot) {
150             this.snapshot = snapshot;
151         }
152
153         @Override
154         public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
155                 @Nonnull final YangInstanceIdentifier path) {
156             final Optional<NormalizedNode<?, ?>> node = snapshot.readNode(path);
157             if (LOG.isTraceEnabled() && node.isPresent()) {
158                 LOG.trace("ConfigSnapshot.read: {}", node.get());
159             }
160             return Futures.immediateCheckedFuture(node);
161         }
162
163         @Override
164         public DataTreeModification newModification() {
165             return snapshot.newModification();
166         }
167     }
168 }
169
170
171