Bug 6170 + Bug 5919 - made FRS cluster-aware
[openflowplugin.git] / applications / forwardingrules-sync / src / main / java / org / opendaylight / openflowplugin / applications / frsync / impl / SimplifiedConfigListener.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 org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
15 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
18 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
19 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
20 import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
23 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Listens to config changes and delegates add/remove/update/barrier to {@link SyncReactor}.
29  */
30 public class SimplifiedConfigListener extends AbstractFrmSyncListener<FlowCapableNode> {
31     private static final Logger LOG = LoggerFactory.getLogger(SimplifiedConfigListener.class);
32     private final SyncReactor reactor;
33     private final FlowCapableNodeSnapshotDao configSnapshot;
34     private final FlowCapableNodeDao operationalDao;
35
36     public SimplifiedConfigListener(final SyncReactor reactor,
37                                     final FlowCapableNodeSnapshotDao configSnapshot,
38                                     final FlowCapableNodeDao operationalDao) {
39         this.reactor = reactor;
40         this.configSnapshot = configSnapshot;
41         this.operationalDao = operationalDao;
42     }
43
44     @Override
45     public void onDataTreeChanged(Collection<DataTreeModification<FlowCapableNode>> modifications) {
46         LOG.trace("Config changes: {}", modifications.size());
47         super.onDataTreeChanged(modifications);
48     }
49
50     /**
51      * Compare cached operational with current config modification. If operational is not present
52      * skip calling Inventory RPCs.
53      * @throws InterruptedException from syncup
54      */
55     protected Optional<ListenableFuture<Boolean>> processNodeModification(
56             DataTreeModification<FlowCapableNode> modification) throws InterruptedException {
57         final InstanceIdentifier<FlowCapableNode> nodePath = modification.getRootPath().getRootIdentifier();
58         final NodeId nodeId = PathUtil.digNodeId(nodePath);
59
60         configSnapshot.updateCache(nodeId, Optional.fromNullable(modification.getRootNode().getDataAfter()));
61
62         final Optional<FlowCapableNode> operationalNode = operationalDao.loadByNodeId(nodeId);
63         if (!operationalNode.isPresent()) {
64             LOG.info("Skip syncup, {} operational is not present", nodeId.getValue());
65             return Optional.absent();
66         }
67
68         final DataObjectModification<FlowCapableNode> configModification = modification.getRootNode();
69         final FlowCapableNode dataBefore = configModification.getDataBefore();
70         final FlowCapableNode dataAfter = configModification.getDataAfter();
71         final ListenableFuture<Boolean> endResult;
72         if (dataBefore == null && dataAfter != null) {
73             endResult = onNodeAdded(nodePath, dataAfter, operationalNode.get());
74         } else if (dataBefore != null && dataAfter == null) {
75             endResult = onNodeDeleted(nodePath, dataBefore);
76         } else {
77             endResult = onNodeUpdated(nodePath, dataBefore, dataAfter);
78         }
79
80         return Optional.of(endResult);
81     }
82
83     /**
84      * Add only what is missing in operational store. Config. node could be added in two situations:
85      * <ul>
86      * <li>Note very first time after restart was handled by operational listener. Syncup should
87      * calculate no delta (we don want to reconfigure switch if not necessary).</li>
88      * <li>But later the config. node could be deleted, after that config node added again. Syncup
89      * should calculate that everything needs to be added. Operational store should be empty in
90      * optimal case (but the switch could be reprogrammed by another person/system.</li>
91      * </ul>
92      */
93     private ListenableFuture<Boolean> onNodeAdded(final InstanceIdentifier<FlowCapableNode> nodePath,
94                                                   final FlowCapableNode dataAfter,
95                                                   final FlowCapableNode operationalNode) throws InterruptedException {
96         NodeId nodeId = PathUtil.digNodeId(nodePath);
97         LOG.trace("onNodeAdded {}", nodeId);
98         return reactor.syncup(nodePath, dataAfter, operationalNode, dsType());
99     }
100
101     /**
102      * Apply minimal changes very fast. For better performance needed just compare config
103      * after+before. Config listener should not be dependent on operational flows/groups while
104      * updating config because operational store is highly async and it depends on another module in
105      * system which is updating operational store (that components is also trying to solve
106      * scale/performance issues on several layers).
107      */
108     private ListenableFuture<Boolean> onNodeUpdated(final InstanceIdentifier<FlowCapableNode> nodePath,
109                                                     final FlowCapableNode dataBefore,
110                                                     final FlowCapableNode dataAfter) throws InterruptedException {
111         NodeId nodeId = PathUtil.digNodeId(nodePath);
112         LOG.trace("onNodeUpdated {}", nodeId);
113         return reactor.syncup(nodePath, dataAfter, dataBefore, dsType());
114     }
115
116     /**
117      * Remove values that are being deleted in the config from the switch. Note, this could be
118      * probably optimized using dedicated wipe-out RPC, but it has impact on switch if it is
119      * programmed by two person/system
120      */
121     private ListenableFuture<Boolean> onNodeDeleted(final InstanceIdentifier<FlowCapableNode> nodePath,
122                                                     final FlowCapableNode dataBefore) throws InterruptedException {
123         NodeId nodeId = PathUtil.digNodeId(nodePath);
124         LOG.trace("onNodeDeleted {}", nodeId);
125         return reactor.syncup(nodePath, null, dataBefore, dsType());
126     }
127
128     @Override
129     public LogicalDatastoreType dsType() {
130         return LogicalDatastoreType.CONFIGURATION;
131     }
132
133 }