Merge "Bug 5577 Retry mechanism"
[openflowplugin.git] / applications / forwardingrules-sync / src / main / java / org / opendaylight / openflowplugin / applications / frsync / impl / SimplifiedOperationalListener.java
1 /**
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.openflowplugin.applications.frsync.impl;
10
11 import com.google.common.base.Optional;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.util.Collection;
14 import java.util.List;
15 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
16 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
17 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
20 import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
21 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
22 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
29 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Listens to operational new nodes and delegates add/remove/update/barrier to {@link SyncReactor}.
35  */
36 public class SimplifiedOperationalListener extends AbstractFrmSyncListener<Node> {
37     private static final Logger LOG = LoggerFactory.getLogger(SimplifiedOperationalListener.class);
38
39     protected final SyncReactor reactor;
40     protected final FlowCapableNodeSnapshotDao operationalSnapshot;
41     protected final FlowCapableNodeDao configDao;
42
43     public SimplifiedOperationalListener(SyncReactor reactor, FlowCapableNodeSnapshotDao operationalSnapshot,
44                                          FlowCapableNodeDao configDao) {
45         this.reactor = reactor;
46         this.operationalSnapshot = operationalSnapshot;
47         this.configDao = configDao;
48     }
49
50     @Override
51     public void onDataTreeChanged(Collection<DataTreeModification<Node>> modifications) {
52         LOG.trace("Inventory Operational changes {}", modifications.size());
53         super.onDataTreeChanged(modifications);
54     }
55
56     /**
57      * This method behaves like this:
58      * <ul>
59      * <li>If node is added to operational store then reconciliation.</li>
60      * <li>Node is deleted from operational cache is removed.</li>
61      * <li>Skip this event otherwise.</li>
62      * </ul>
63      *
64      * @throws InterruptedException from syncup
65      */
66     protected Optional<ListenableFuture<Boolean>> processNodeModification(
67             DataTreeModification<Node> modification) throws ReadFailedException, InterruptedException {
68
69         updateCache(modification);
70         if (isReconciliationNeeded(modification)) {
71             return reconciliation(modification);
72         }
73         return skipModification(modification);
74     }
75
76     /**
77      * Remove if delete. Update only if FlowCapableNode Augmentation modified.
78      *
79      * @param modification Datastore modification
80      * @return true for cache update, false for cache remove
81      */
82     protected boolean updateCache(DataTreeModification<Node> modification) {
83         if (isDelete(modification) || isDeleteLogical(modification)) {
84             operationalSnapshot.updateCache(nodeId(modification), Optional.<FlowCapableNode>absent());
85             return false;
86         }
87         operationalSnapshot.updateCache(nodeId(modification), Optional.fromNullable(flowCapableNodeAfter(modification)));
88         return true;
89     }
90
91     private Optional<ListenableFuture<Boolean>> skipModification(DataTreeModification<Node> modification) {
92         LOG.trace("Skipping Inventory Operational modification {}, before {}, after {}", nodeIdValue(modification),
93                 modification.getRootNode().getDataBefore() == null ? "null" : "nonnull",
94                 modification.getRootNode().getDataAfter() == null ? "null" : "nonnull");
95         return Optional.absent();
96     }
97
98     /**
99      * ModificationType.DELETE
100      */
101     private boolean isDelete(DataTreeModification<Node> modification) {
102         if (ModificationType.DELETE == modification.getRootNode().getModificationType()) {
103             LOG.trace("Delete {} (physical)", nodeIdValue(modification));
104             return true;
105         }
106
107         return false;
108     }
109
110     /**
111      * All connectors disappeared from operational store (logical delete).
112      */
113     private boolean isDeleteLogical(DataTreeModification<Node> modification) {
114         final DataObjectModification<Node> rootNode = modification.getRootNode();
115         if (!safeConnectorsEmpty(rootNode.getDataBefore()) && safeConnectorsEmpty(rootNode.getDataAfter())) {
116             LOG.trace("Delete {} (logical)", nodeIdValue(modification));
117             return true;
118         }
119
120         return false;
121     }
122
123     private boolean isAdd(DataTreeModification<Node> modification) {
124         final DataObjectModification<Node> rootNode = modification.getRootNode();
125         final Node dataAfter = rootNode.getDataAfter();
126         final Node dataBefore = rootNode.getDataBefore();
127
128         final boolean nodeAppearedInOperational = dataBefore == null && dataAfter != null;
129         if (nodeAppearedInOperational) {
130             LOG.trace("Add {} (physical)", nodeIdValue(modification));
131         }
132         return nodeAppearedInOperational;
133     }
134
135     /**
136      * All connectors appeared in operational store (logical add).
137      */
138     private boolean isAddLogical(DataTreeModification<Node> modification) {
139         final DataObjectModification<Node> rootNode = modification.getRootNode();
140         if (safeConnectorsEmpty(rootNode.getDataBefore()) && !safeConnectorsEmpty(rootNode.getDataAfter())) {
141             LOG.trace("Add {} (logical)", nodeIdValue(modification));
142             return true;
143         }
144
145         return false;
146     }
147
148     protected boolean isReconciliationNeeded(DataTreeModification<Node> modification) {
149         return isAdd(modification) || isAddLogical(modification);
150     }
151
152     private Optional<ListenableFuture<Boolean>> reconciliation(DataTreeModification<Node> modification) throws InterruptedException {
153         final NodeId nodeId = nodeId(modification);
154         final Optional<FlowCapableNode> nodeConfiguration = configDao.loadByNodeId(nodeId);
155
156         if (nodeConfiguration.isPresent()) {
157             LOG.debug("Reconciliation: {}", nodeId.getValue());
158             final InstanceIdentifier<FlowCapableNode> nodePath = InstanceIdentifier.create(Nodes.class)
159                     .child(Node.class, new NodeKey(nodeId(modification))).augmentation(FlowCapableNode.class);
160             return Optional.of(reactor.syncup(nodePath, nodeConfiguration.get(), flowCapableNodeAfter(modification), dsType()));
161         } else {
162             return skipModification(modification);
163         }
164     }
165
166     static FlowCapableNode flowCapableNodeAfter(DataTreeModification<Node> modification) {
167         final Node dataAfter = modification.getRootNode().getDataAfter();
168         if (dataAfter == null) {
169             return null;
170         }
171         return dataAfter.getAugmentation(FlowCapableNode.class);
172     }
173
174     static boolean safeConnectorsEmpty(Node node) {
175         if (node == null) {
176             return true;
177         }
178
179         final List<NodeConnector> nodeConnectors = node.getNodeConnector();
180
181         return nodeConnectors == null || nodeConnectors.isEmpty();
182     }
183
184     static String nodeIdValue(DataTreeModification<Node> modification) {
185         final NodeId nodeId = nodeId(modification);
186
187         if (nodeId == null) {
188             return null;
189         }
190
191         return nodeId.getValue();
192     }
193
194     static NodeId nodeId(DataTreeModification<Node> modification) {
195         final DataObjectModification<Node> rootNode = modification.getRootNode();
196         final Node dataAfter = rootNode.getDataAfter();
197
198         if (dataAfter != null) {
199             return dataAfter.getId();
200         }
201
202         final Node dataBefore = rootNode.getDataBefore();
203         if (dataBefore != null) {
204             return dataBefore.getId();
205         }
206
207         return null;
208     }
209
210     @Override
211     public LogicalDatastoreType dsType() {
212         return LogicalDatastoreType.OPERATIONAL;
213     }
214 }