bug 8029 added ref counts for physical locators.
[ovsdb.git] / hwvtepsouthbound / hwvtepsouthbound-impl / src / main / java / org / opendaylight / ovsdb / hwvtepsouthbound / transact / AbstractTransactCommand.java
1 /*
2  * Copyright © 2015, 2017 China Telecom Beijing Research Institute 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.ovsdb.hwvtepsouthbound.transact;
10
11 import java.lang.reflect.ParameterizedType;
12 import java.lang.reflect.Type;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Objects;
21 import java.util.Set;
22 import java.util.concurrent.ConcurrentHashMap;
23
24 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
28 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil;
29 import org.opendaylight.ovsdb.lib.notation.UUID;
30 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
31 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
35 import org.opendaylight.yangtools.yang.binding.Augmentation;
36 import org.opendaylight.yangtools.yang.binding.Identifiable;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 public abstract class AbstractTransactCommand<T extends Identifiable, Aug extends Augmentation<Node>> implements TransactCommand<T> {
42
43     private static final Logger LOG = LoggerFactory.getLogger(AbstractTransactCommand.class);
44     protected static final UUID TXUUID = new UUID("TXUUID");
45     protected ThreadLocal<HwvtepOperationalState> threadLocalOperationalState = new ThreadLocal<>();
46     protected ThreadLocal<TransactionBuilder> threadLocalDeviceTransaction = new ThreadLocal<>();
47     private Collection<DataTreeModification<Node>> changes;
48     protected Map<TransactionBuilder, List<MdsalUpdate<T>>> updates = new ConcurrentHashMap<>();
49
50     protected AbstractTransactCommand() {
51         // NO OP
52     }
53
54     public AbstractTransactCommand(HwvtepOperationalState state, Collection<DataTreeModification<Node>> changes) {
55         this.threadLocalOperationalState.set(state);
56         this.changes = changes;
57     }
58
59     public HwvtepOperationalState getOperationalState() {
60         return threadLocalOperationalState.get();
61     }
62
63     public Collection<DataTreeModification<Node>> getChanges() {
64         return changes;
65     }
66
67     void updateCurrentTxDeleteData(Class<? extends Identifiable> cls, InstanceIdentifier key, T data) {
68         getOperationalState().getDeviceInfo().markKeyAsInTransit(cls, key);
69         addToUpdates(key, data);
70         getOperationalState().getDeviceInfo().clearConfigData(cls, key);
71     }
72
73     void updateCurrentTxData(Class<? extends Identifiable> cls, InstanceIdentifier key, UUID uuid, T data) {
74         getOperationalState().getDeviceInfo().markKeyAsInTransit(cls, key);
75         addToUpdates(key, data);
76         getOperationalState().getDeviceInfo().updateConfigData(cls, key, data);
77     }
78
79     void addToUpdates(InstanceIdentifier key, T data) {
80         T oldData = null;
81         Type type = getClass().getGenericSuperclass();
82         Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
83         if (getDeviceInfo().getConfigData((Class<? extends Identifiable>) classType, key) != null) {
84             oldData = (T) getDeviceInfo().getConfigData((Class<? extends Identifiable>) classType, key);
85         }
86         updates.putIfAbsent(getDeviceTransaction(), new ArrayList<MdsalUpdate<T>>());
87         updates.get(getDeviceTransaction()).add(new MdsalUpdate<T>(key, data, oldData));
88     }
89
90     void processDependencies(final UnMetDependencyGetter<T> unMetDependencyGetter,
91                              final TransactionBuilder transaction,
92                              final InstanceIdentifier<Node> nodeIid,
93                              final InstanceIdentifier key,
94                              final T data, final Object... extraData) {
95
96         this.threadLocalDeviceTransaction.set(transaction);
97         HwvtepDeviceInfo deviceInfo = getOperationalState().getDeviceInfo();
98         Map inTransitDependencies = new HashMap<>();
99         Map confingDependencies = new HashMap<>();
100
101         if (!isRemoveCommand() && unMetDependencyGetter != null) {
102             inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(getOperationalState(), data);
103             confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(getOperationalState(), data);
104             //we can skip the config termination point dependency as we can create them in device as part of this tx
105             confingDependencies.remove(TerminationPoint.class);
106         }
107
108         Type type = getClass().getGenericSuperclass();
109         Type classType = ((ParameterizedType)type).getActualTypeArguments()[0];
110
111         //If this key itself is in transit wait for the response of this key itself
112         if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
113             inTransitDependencies.put(classType, Collections.singletonList(key));
114         }
115
116         if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies) && HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
117             doDeviceTransaction(transaction, nodeIid, data, key, extraData);
118             if (isRemoveCommand()) {
119                 getDeviceInfo().clearConfigData((Class<? extends Identifiable>) classType, key);
120             } else {
121                 getDeviceInfo().updateConfigData((Class<? extends Identifiable>) classType, key, data);
122             }
123         }
124         if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
125             DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
126                     key, data, confingDependencies) {
127
128                 @Override
129                 public void onDependencyResolved(HwvtepOperationalState operationalState,
130                                                  TransactionBuilder transactionBuilder) {
131                     AbstractTransactCommand.this.threadLocalOperationalState.set(operationalState);
132                     AbstractTransactCommand.this.threadLocalDeviceTransaction.set(transactionBuilder);
133                     onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
134                 }
135
136                 public void onFailure() {
137                     AbstractTransactCommand.this.onFailure(getDeviceTransaction());
138                 }
139
140                 public void onSuccess() {
141                     AbstractTransactCommand.this.onSuccess(getDeviceTransaction());
142                 }
143             };
144             deviceInfo.addJobToQueue(configWaitingJob);
145         }
146         if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
147
148             DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
149                     key, data, inTransitDependencies) {
150
151                 @Override
152                 public void onDependencyResolved(HwvtepOperationalState operationalState,
153                                                  TransactionBuilder transactionBuilder) {
154                     //data would have got deleted by , push the data only if it is still in configds
155                     threadLocalOperationalState.set(operationalState);
156                     threadLocalDeviceTransaction.set(transactionBuilder);
157                     T data = (T)new MdsalUtils(operationalState.getDataBroker()).read(
158                             LogicalDatastoreType.CONFIGURATION, key);
159                     if (data != null) {
160                         onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
161                     } else {
162                         LOG.warn("Skipping add of key: {} as it is not present txId: {}", key);
163                     }
164                 }
165
166                 public void onFailure() {
167                     AbstractTransactCommand.this.onFailure(getDeviceTransaction());
168                 }
169
170                 public void onSuccess() {
171                     AbstractTransactCommand.this.onSuccess(getDeviceTransaction());
172                 }
173             };
174             deviceInfo.addJobToQueue(opWaitingJob);
175         }
176     }
177
178     public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
179                                     InstanceIdentifier key, Object... extraData) {
180         //tobe removed as part of refactoring patch
181     }
182
183     public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
184                                InstanceIdentifier key, Object... extraData) {
185         //tobe removed as part of refactoring patch
186     }
187
188     protected Aug getAugmentation(Node node) {
189         if (node == null) {
190             return null;
191         }
192         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
193         Class<? extends Augmentation<Node>> augType = (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
194         Augmentation<Node> augmentation = node.getAugmentation(augType);
195         return (Aug)augmentation;
196     }
197
198     protected List<T> getData(Aug augmentation) {
199         return Collections.EMPTY_LIST;
200     }
201
202     protected List<T> getData(Node node) {
203         Aug augmentation = getAugmentation(node);
204         if (augmentation != null) {
205             List<T> data = getData(augmentation);
206             if (data != null) {
207                 return new ArrayList<>(data);
208             }
209         }
210         return Collections.emptyList();
211     }
212
213     protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
214             Collection<DataTreeModification<Node>> changes, Class<T> class1) {
215         Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
216         if (changes != null && !changes.isEmpty()) {
217             for (DataTreeModification<Node> change : changes) {
218                 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
219                 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
220                 List<T> removed;
221                 if (getOperationalState().isInReconciliation()) {
222                     removed = getRemoved(change);
223                 } else {
224                     removed = (List<T>) getOperationalState().getDeletedData(key, classType);
225                 }
226                 removed.addAll(getCascadeDeleteData(change));
227                 result.put(key, removed);
228             }
229         }
230         return result;
231     }
232
233     protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
234             Collection<DataTreeModification<Node>> changes, Class<T> class1) {
235         Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
236         if (changes != null && !changes.isEmpty()) {
237             for (DataTreeModification<Node> change : changes) {
238                 InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
239                 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
240                 List<T> updated = null;
241                 if (getOperationalState().isInReconciliation()) {
242                     updated = getUpdated(change);
243                 } else {
244                     updated = (List<T>) getOperationalState().getUpdatedData(key, classType);
245                 }
246                 result.put(key, updated);
247             }
248         }
249         return result;
250     }
251
252     List<T>  getCascadeDeleteData(DataTreeModification<Node> change) {
253         if (!cascadeDelete()) {
254             return Collections.EMPTY_LIST;
255         }
256         DataObjectModification<Node> mod = change.getRootNode();
257         Node updatedNode = TransactUtils.getUpdated(mod);
258         List<T> updatedData = getData(updatedNode);
259         Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
260         UnMetDependencyGetter dependencyGetter = getDependencyGetter();
261         if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData) && dependencyGetter != null) {
262             List<T> removed = new ArrayList<>();
263             for (T ele : updatedData) {
264                 if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
265                     removed.add(ele);
266                 }
267             }
268             return removed;
269         }
270         return Collections.EMPTY_LIST;
271     }
272
273     List<T> getRemoved(DataTreeModification<Node> change) {
274         DataObjectModification<Node> mod = change.getRootNode();
275
276         Node removed = TransactUtils.getRemoved(mod);
277         Node updated = TransactUtils.getUpdated(mod);
278         Node before = mod.getDataBefore();
279         return diffOf(removed, before, updated, true);
280     }
281
282     List<T> getUpdated(DataTreeModification<Node> change) {
283         DataObjectModification<Node> mod = change.getRootNode();
284         Node updated = TransactUtils.getUpdated(mod);
285         Node before = mod.getDataBefore();
286         return diffOf(updated, before, false);
287     }
288
289     List<T> diffOf(Node include, Node a, Node b, boolean compareKeyOnly) {
290         List<T> data1 = getData(include);
291         List<T> data2 = diffOf(a, b, compareKeyOnly);
292         if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
293             return Collections.emptyList();
294         }
295         List<T> result = new ArrayList<>(data1);
296         result.addAll(data2);
297         return result;
298     }
299
300     List<T> diffOf(Node a, Node b, boolean compareKeyOnly) {
301         List<T> result = new ArrayList<>();
302
303         List<T> list1 = getData(a);
304         List<T> list2 = getData(b);
305
306         if (HwvtepSouthboundUtil.isEmpty(list1)) {
307             return Collections.emptyList();
308         }
309         if (HwvtepSouthboundUtil.isEmpty(list2)) {
310             return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.emptyList() : list1;
311         }
312
313         Iterator<T> it1 = list1.iterator();
314
315         while(it1.hasNext()) {
316             T ele = it1.next();
317             Iterator<T> it2 = list2.iterator();
318             boolean found = false;
319             while (it2.hasNext()) {
320                 T other  = it2.next();
321                 found = compareKeyOnly ? Objects.equals(ele.getKey(), other.getKey()) : areEqual(ele, other);
322                 if ( found ) {
323                     it2.remove();
324                     break;
325                 }
326             }
327             if (!found) {
328                 result.add(ele);
329             }
330         }
331         return result;
332     }
333
334
335     protected Type getClassType() {
336         Type type = getClass().getGenericSuperclass();
337         Type classType = ((ParameterizedType)type).getActualTypeArguments()[0];
338         return classType;
339     }
340
341     protected boolean areEqual(T a , T b) {
342         return a.getKey().equals(b.getKey());
343     }
344
345     protected UnMetDependencyGetter getDependencyGetter() {
346         return null;
347     }
348
349     /**
350      * Tells if this object needs to be deleted if its dependent object gets deleted
351      * Ex : LocalUcastMac and LocalMacstMac
352      * @return true if this object needs to be deleted if its dependent object gets deleted
353      */
354     protected boolean cascadeDelete() {
355         return false;
356     }
357
358     protected boolean isRemoveCommand() {
359         return false;
360     }
361
362     protected HwvtepDeviceInfo getDeviceInfo() {
363         return getOperationalState().getDeviceInfo();
364     }
365
366     protected TransactionBuilder getDeviceTransaction() {
367         return threadLocalDeviceTransaction.get();
368     }
369
370     public void onSuccess(TransactionBuilder deviceTransaction) {
371         if (deviceTransaction == null || !updates.containsKey(deviceTransaction)) {
372             return;
373         }
374         onCommandSucceeded();
375     }
376
377     public void onFailure(TransactionBuilder deviceTransaction) {
378         if (deviceTransaction == null || !updates.containsKey(deviceTransaction)) {
379             return;
380         }
381         for (MdsalUpdate mdsalUpdate : updates.get(deviceTransaction)) {
382             getDeviceInfo().clearInTransit((Class<? extends Identifiable>)mdsalUpdate.getClass(), mdsalUpdate.getKey());
383         }
384         onCommandFailed();
385     }
386
387     protected void onCommandSucceeded() {
388     }
389
390     protected void onCommandFailed() {
391     }
392 }