daac0a84490e525cf8bfa4972fdf41559e597620
[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 javax.annotation.Nonnull;
15 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
16 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
19 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
20 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
21 import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
22 import org.opendaylight.openflowplugin.applications.frsync.util.SyncupEntry;
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.yangtools.yang.binding.InstanceIdentifier;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * Listens to config changes and delegates sync entry to {@link SyncReactor}.
31  */
32 public class SimplifiedConfigListener extends AbstractFrmSyncListener<FlowCapableNode> {
33
34     private static final Logger LOG = LoggerFactory.getLogger(SimplifiedConfigListener.class);
35     private final SyncReactor reactor;
36     private final FlowCapableNodeSnapshotDao configSnapshot;
37     private final FlowCapableNodeDao operationalDao;
38
39     public SimplifiedConfigListener(final SyncReactor reactor,
40                                     final FlowCapableNodeSnapshotDao configSnapshot,
41                                     final FlowCapableNodeDao operationalDao) {
42         this.reactor = reactor;
43         this.configSnapshot = configSnapshot;
44         this.operationalDao = operationalDao;
45     }
46
47     @Override
48     public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<FlowCapableNode>> modifications) {
49         super.onDataTreeChanged(modifications);
50     }
51
52     /**
53      * Update cache. If operational data are present, choose appropriate data and start syncup.
54      * Otherwise skip incoming change.
55      */
56     @Override
57     protected Optional<ListenableFuture<Boolean>> processNodeModification(
58             final DataTreeModification<FlowCapableNode> modification) {
59         final InstanceIdentifier<FlowCapableNode> nodePath = modification.getRootPath().getRootIdentifier();
60         final NodeId nodeId = PathUtil.digNodeId(nodePath);
61
62         configSnapshot.updateCache(nodeId, Optional.fromNullable(modification.getRootNode().getDataAfter()));
63
64         final Optional<FlowCapableNode> operationalNode = operationalDao.loadByNodeId(nodeId);
65         if (!operationalNode.isPresent()) {
66             LOG.debug("Skip syncup, {} operational is not present", nodeId.getValue());
67             return Optional.absent();
68         }
69
70         final DataObjectModification<FlowCapableNode> configModification = modification.getRootNode();
71         final FlowCapableNode dataBefore = configModification.getDataBefore();
72         final FlowCapableNode dataAfter = configModification.getDataAfter();
73         final ListenableFuture<Boolean> endResult;
74         if (dataBefore == null && dataAfter != null) {
75             endResult = onNodeAdded(nodePath, dataAfter, operationalNode.get());
76         } else if (dataBefore != null && dataAfter == null) {
77             endResult = onNodeDeleted(nodePath, dataBefore);
78         } else {
79             endResult = onNodeUpdated(nodePath, dataBefore, dataAfter);
80         }
81
82         return Optional.of(endResult);
83     }
84
85     /**
86      * If node was added to config DS and it is already present in operational DS (connected) diff between current
87      * new configuration and actual configuration (seen in operational) should be calculated and sent to device.
88      */
89     private ListenableFuture<Boolean> onNodeAdded(final InstanceIdentifier<FlowCapableNode> nodePath,
90                                                   final FlowCapableNode dataAfter,
91                                                   final FlowCapableNode operationalNode) {
92         LOG.debug("Reconciliation {}: {}", dsType(), PathUtil.digNodeId(nodePath).getValue());
93         final SyncupEntry syncupEntry = new SyncupEntry(dataAfter, dsType(), operationalNode,
94                 LogicalDatastoreType.OPERATIONAL);
95         return reactor.syncup(nodePath, syncupEntry);
96     }
97
98     /**
99      * Apply minimal changes very fast. For better performance needed just compare config
100      * after+before. Config listener should not be dependent on operational flows/groups/meters while
101      * updating config because operational store is highly async and it depends on another module in
102      * system which is updating operational store (that components is also trying to solve
103      * scale/performance issues on several layers).
104      */
105     private ListenableFuture<Boolean> onNodeUpdated(final InstanceIdentifier<FlowCapableNode> nodePath,
106                                                     final FlowCapableNode dataBefore,
107                                                     final FlowCapableNode dataAfter) {
108         final SyncupEntry syncupEntry = new SyncupEntry(dataAfter, dsType(), dataBefore, dsType());
109         return reactor.syncup(nodePath, syncupEntry);
110     }
111
112     /**
113      * Remove values that are being deleted in the config from the switch.
114      * Note, this could be probably optimized using dedicated wipe-out RPC.
115      */
116     private ListenableFuture<Boolean> onNodeDeleted(final InstanceIdentifier<FlowCapableNode> nodePath,
117                                                     final FlowCapableNode dataBefore) {
118         final SyncupEntry syncupEntry = new SyncupEntry(null, dsType(), dataBefore, dsType());
119         return reactor.syncup(nodePath, syncupEntry);
120     }
121
122     @Override
123     public LogicalDatastoreType dsType() {
124         return LogicalDatastoreType.CONFIGURATION;
125     }
126 }