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