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