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