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