NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / listeners / ChildListener.java
1 /*
2  * Copyright © 2017 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.netvirt.elan.l2gw.listeners;
9
10 import java.util.Collection;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.concurrent.ConcurrentHashMap;
14 import javax.annotation.PreDestroy;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.opendaylight.genius.datastoreutils.TaskRetryLooper;
17 import org.opendaylight.mdsal.binding.api.DataBroker;
18 import org.opendaylight.mdsal.binding.api.DataObjectModification;
19 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
20 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
21 import org.opendaylight.mdsal.binding.api.DataTreeModification;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
24 import org.opendaylight.yangtools.concepts.ListenerRegistration;
25 import org.opendaylight.yangtools.yang.binding.DataObject;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * ChildListener class listens to provided P node and its child node C.
32  * It can act on some special condition based on the parameter set.
33  * When the children are updated and same update is processed by parent listener , it
34  * takes a toll on the performance , specially in cases where child updates are too often .
35  * In case there are any Sub-Tree modification , it can be handled and not handled based on the parameters.
36  * @param <P> - Parent DataObject
37  * @param <C> - Child DataObject (Subtree)
38  * @param <G> - Group type (Mostly key of Subtree DataObject)
39  */
40 public abstract class ChildListener<P extends DataObject, C extends DataObject, G>
41         implements DataTreeChangeListener<P>, AutoCloseable {
42
43     private static final Logger LOG = LoggerFactory.getLogger(ChildListener.class);
44     private static final long STARTUP_LOOP_TICK = 500;
45     private static final int STARTUP_LOOP_MAX_RETRIES = 8;
46
47     protected final DataBroker dataBroker;
48     private ListenerRegistration<?> registration;
49     private final boolean processParentDeletes;
50
51     public ChildListener(DataBroker dataBroker, boolean processParentDeletes) {
52         this.dataBroker = dataBroker;
53         this.processParentDeletes = processParentDeletes;
54     }
55
56     public void init() throws Exception {
57         registration = registerListener(LogicalDatastoreType.OPERATIONAL, getParentWildCardPath());
58     }
59
60     protected ListenerRegistration<?> registerListener(final LogicalDatastoreType dsType,
61                                                  final InstanceIdentifier wildCard) throws Exception {
62         DataTreeIdentifier<P> treeId = DataTreeIdentifier.create(dsType, wildCard);
63         TaskRetryLooper looper = new TaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
64         return looper.loopUntilNoException(() -> dataBroker.registerDataTreeChangeListener(treeId, this));
65     }
66
67     /**
68      * Upon parent added will be called.
69      * @param parent - Added parent node
70      */
71     protected abstract void onParentAdded(DataTreeModification<P> parent);
72
73     /**
74      * Upon parent delete will be called.
75      * @param parent - Deleted parent
76      */
77     protected abstract void onParentRemoved(InstanceIdentifier<P> parent);
78
79     /**
80      * Based on the return type of this function , processing on the dataTreeChanges takes place.
81      * @param parent - Parent node which is add/update/delete
82      * @return boolean value .
83      */
84     protected abstract boolean proceed(InstanceIdentifier<P> parent);
85
86     /**
87      * Group of data for bulk child update cases.
88      * @param child - Subtree Node data
89      * @return - Group value
90      */
91     protected abstract G getGroup(C child);
92
93     /**
94      * Process the added/updated/Deleted subtree data.
95      * @param updatedMacsGrouped - Updated Subtree Data
96      * @param deletedMacsGrouped - Deleted Subtree Data
97      */
98     protected abstract void onUpdate(
99             Map<G, Map<InstanceIdentifier, C>> updatedMacsGrouped,
100             Map<G, Map<InstanceIdentifier, C>> deletedMacsGrouped);
101
102     /**
103      * Returns the all subtree data modified.
104      * @param parentIid - Parent Node iid
105      * @param mod - Modified Data
106      * @return subtree modified data map with IID
107      */
108     protected abstract Map<InstanceIdentifier<C>, DataObjectModification<C>> getChildMod(
109             InstanceIdentifier<P> parentIid,
110             DataObjectModification<P> mod);
111
112     protected abstract InstanceIdentifier<Node> getParentWildCardPath();
113
114     @Override
115     @PreDestroy
116     public void close() {
117         if (registration != null) {
118             registration.close();
119         }
120     }
121
122     @Override
123     public void onDataTreeChanged(final Collection<DataTreeModification<P>> changes) {
124         Map<G, Map<InstanceIdentifier, C>> updatedData = new HashMap<>();
125         Map<G, Map<InstanceIdentifier, C>> deletedData = new HashMap<>();
126         extractUpdatedAndDeletedMacs(changes, updatedData, deletedData);
127         onUpdate(updatedData, deletedData);
128     }
129
130     /**
131      * Taked the data changes and process them based on Add/Delete/Update or Subtree Modification.
132      * @param changes - Data tree changes
133      * @param updatedMacsGrouped - Updated Subtree Data
134      * @param deletedMacsGrouped - Deleted Subtree Data
135      */
136     void extractUpdatedAndDeletedMacs(Collection<DataTreeModification<P>> changes,
137                                       Map<G, Map<InstanceIdentifier, C>> updatedMacsGrouped,
138                                       Map<G, Map<InstanceIdentifier, C>> deletedMacsGrouped) {
139         changes.stream()
140                 .filter(change -> proceed(change.getRootPath().getRootIdentifier()))
141                 .forEach((change) -> {
142                     InstanceIdentifier<P> iid = change.getRootPath().getRootIdentifier();
143                     DataObjectModification<P> modification = change.getRootNode();
144                     switch (getModificationType(modification)) {
145                         case WRITE:
146                             if (modification.getDataBefore() == null) {
147                                 onParentAdded(change);
148                             } else {
149                                 LOG.info("Unexpected write to parent before {}", modification.getDataBefore());
150                                 LOG.info("Unexpected write to parent after {}", modification.getDataAfter());
151                             }
152                             extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
153                             break;
154                         case SUBTREE_MODIFIED:
155                             extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
156                             break;
157                         case DELETE:
158                             if (processParentDeletes) {
159                                 extractDataChanged(iid, modification, updatedMacsGrouped, deletedMacsGrouped);
160                             }
161                             onParentRemoved(iid);
162                             //Do not process the disconnected nodes
163                             break;
164                         default:
165                             break;
166                     }
167                 });
168     }
169
170     /**
171      * Only when Subtree modification happen this is called .
172      * @param key - IID of child data
173      * @param parentModification - Modified data
174      * @param updatedMacsGrouped - Subtree updated data
175      * @param deletedMacsGrouped - Subtree deleted datas
176      */
177     private void extractDataChanged(final InstanceIdentifier<P> key,
178                                     final DataObjectModification<P> parentModification,
179                                     final Map<G, Map<InstanceIdentifier, C>> updatedMacsGrouped,
180                                     final Map<G, Map<InstanceIdentifier, C>> deletedMacsGrouped) {
181
182         Map<InstanceIdentifier<C>, DataObjectModification<C>> children = getChildMod(key, parentModification);
183         for (Map.Entry<InstanceIdentifier<C>, DataObjectModification<C>> entry : children.entrySet()) {
184             DataObjectModification<C> childMod = entry.getValue();
185             InstanceIdentifier<C> childIid = entry.getKey();
186             DataObjectModification.ModificationType modificationType = getModificationType(childMod);
187             G group;
188             C dataBefore = childMod.getDataBefore();
189             C dataAfter = childMod.getDataAfter();
190             switch (modificationType) {
191                 case WRITE:
192                 case SUBTREE_MODIFIED:
193                     group = getGroup(dataAfter);
194                     updatedMacsGrouped.computeIfAbsent(group, (grp) -> new ConcurrentHashMap<>());
195                     updatedMacsGrouped.get(group).put(childIid, dataAfter);
196                     break;
197                 case DELETE:
198                     group = getGroup(dataBefore);
199                     deletedMacsGrouped.computeIfAbsent(group, (grp) -> new ConcurrentHashMap<>());
200                     deletedMacsGrouped.get(group).put(childIid, dataBefore);
201                     break;
202                 default:
203                     break;
204             }
205         }
206     }
207
208     protected DataObjectModification.@Nullable ModificationType getModificationType(
209             final DataObjectModification<? extends DataObject> mod) {
210         try {
211             return mod.getModificationType();
212         } catch (IllegalStateException e) {
213             //LOG.warn("Failed to get the modification type for mod {}", mod);
214         }
215         return null;
216     }
217 }