2422712b61f25d30d9fb396551921c6bac0d4ed4
[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 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
9
10 import com.google.common.collect.Lists;
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.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Objects;
21 import java.util.Set;
22 import java.util.stream.Collectors;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.binding.api.DataObjectModification;
26 import org.opendaylight.mdsal.binding.api.DataTreeModification;
27 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepConnectionInstance;
28 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
29 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil;
30 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepTableReader;
31 import org.opendaylight.ovsdb.lib.notation.UUID;
32 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
33 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
34 import org.opendaylight.ovsdb.utils.mdsal.utils.TransactionType;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
38 import org.opendaylight.yangtools.yang.binding.Augmentation;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.Identifiable;
41 import org.opendaylight.yangtools.yang.binding.Identifier;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 public abstract class AbstractTransactCommand<T extends Identifiable<I> & DataObject, I extends Identifier<T>,
47         A extends Augmentation<Node>> implements TransactCommand<T> {
48
49     private static final Logger LOG = LoggerFactory.getLogger(AbstractTransactCommand.class);
50     protected static final UUID TXUUID = new UUID("TXUUID");
51     protected volatile HwvtepOperationalState hwvtepOperationalState = null;
52     protected volatile TransactionBuilder deviceTransaction = null;
53     private Collection<DataTreeModification<Node>> changes;
54     Set<MdsalUpdate<T>> updates = new HashSet<>();
55
56     protected AbstractTransactCommand() {
57         // NO OP
58     }
59
60     public AbstractTransactCommand(final HwvtepOperationalState state,
61             final Collection<DataTreeModification<Node>> changes) {
62         this.hwvtepOperationalState = state;
63         this.changes = changes;
64     }
65
66     public HwvtepOperationalState getOperationalState() {
67         return hwvtepOperationalState;
68     }
69
70     public DataBroker getDataBroker() {
71         return getOperationalState().getDataBroker();
72     }
73
74     public Collection<DataTreeModification<Node>> getChanges() {
75         return changes;
76     }
77
78     void updateCurrentTxDeleteData(final Class<? extends Identifiable> cls, final InstanceIdentifier key,
79             final T data) {
80         hwvtepOperationalState.updateCurrentTxDeleteData(cls, key);
81         markKeyAsInTransit(cls, key);
82         addToUpdates(key, data);
83     }
84
85     void updateCurrentTxData(final Class<? extends Identifiable> cls, final InstanceIdentifier key, final UUID uuid,
86             final T data) {
87         hwvtepOperationalState.updateCurrentTxData(cls, key, uuid);
88         markKeyAsInTransit(cls, key);
89         addToUpdates(key, data);
90     }
91
92     void addToUpdates(final InstanceIdentifier key, final T data) {
93         T oldData = null;
94         Type type = getClass().getGenericSuperclass();
95         Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
96         if (getConfigData((Class<? extends Identifiable>) classType, key) != null) {
97             oldData = (T) getConfigData((Class<? extends Identifiable>) classType, key).getData();
98         }
99         updates.add(new MdsalUpdate<>(key, data, oldData));
100     }
101
102     void processDependencies(final UnMetDependencyGetter<T> unMetDependencyGetter,
103             final TransactionBuilder transaction,
104             final InstanceIdentifier<Node> nodeIid,
105             final InstanceIdentifier key,
106             final T data, final Object... extraData) {
107
108         HwvtepDeviceInfo deviceInfo = hwvtepOperationalState.getDeviceInfo();
109         Type type = getClass().getGenericSuperclass();
110         Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
111         Map inTransitDependencies = Collections.emptyMap();
112         Map confingDependencies = Collections.emptyMap();
113
114         if (isDeleteCmd()) {
115             if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
116                 inTransitDependencies = new HashMap<>();
117                 inTransitDependencies.put(classType, Lists.newArrayList(key));
118             }
119         } else {
120             inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(hwvtepOperationalState, data);
121             confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(hwvtepOperationalState, data);
122             //we can skip the config termination point dependency as we can create them in device as part of this tx
123             confingDependencies.remove(TerminationPoint.class);
124
125             //If this key itself is in transit wait for the response of this key itself
126             if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)
127                     || deviceInfo.isKeyInDependencyQueue(key)) {
128                 inTransitDependencies.put(classType, Lists.newArrayList(key));
129             }
130         }
131         LOG.info("Update received for key: {} txId: {}", getNodeKeyStr(key), getOperationalState().getTransactionId());
132         if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies)
133                 && HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
134             doDeviceTransaction(transaction, nodeIid, data, key, extraData);
135             if (isDeleteCmd()) {
136                 getDeviceInfo().clearConfigData((Class<? extends Identifiable>) classType, key);
137             } else {
138                 getDeviceInfo().updateConfigData((Class<? extends Identifiable>) classType, key, data);
139             }
140         }
141
142         if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
143             DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
144                     key, data, confingDependencies) {
145                 AbstractTransactCommand clone = getClone();
146
147                 @Override
148                 public void onDependencyResolved(final HwvtepOperationalState operationalState,
149                                                  final TransactionBuilder transactionBuilder) {
150                     clone.hwvtepOperationalState = operationalState;
151                     HwvtepDeviceInfo.DeviceData deviceData =
152                             getDeviceInfo().getConfigData((Class<? extends Identifiable>)getClassType(), key);
153                     T latest = data;
154                     if (deviceData != null && deviceData.getData() != null) {
155                         latest = (T) deviceData.getData();
156                         clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
157                     } else if (isDeleteCmd()) {
158                         clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
159                     }
160                 }
161
162                 @Override
163                 public void onFailure() {
164                     clone.onFailure(transaction);
165                 }
166
167                 @Override
168                 public void onSuccess() {
169                     clone.onSuccess(transaction);
170                 }
171             };
172             LOG.info("Update Adding to config wait queue for key: {} txId: {}",
173                     key, getOperationalState().getTransactionId());
174             addJobToQueue(configWaitingJob);
175             return;
176         }
177         final long transactionId = hwvtepOperationalState.getTransactionId();
178         if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
179
180             DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
181                     key, data, inTransitDependencies, transactionId) {
182                 AbstractTransactCommand clone = getClone();
183
184                 @Override
185                 public void onDependencyResolved(final HwvtepOperationalState operationalState,
186                                                  final TransactionBuilder transactionBuilder) {
187                     clone.hwvtepOperationalState = operationalState;
188                     HwvtepDeviceInfo.DeviceData deviceData = getDeviceInfo()
189                             .getConfigData((Class<? extends Identifiable>)getClassType(), key);
190                     T latest = data;
191                     if (deviceData != null && deviceData.getData() != null) {
192                         latest = (T) deviceData.getData();
193                         clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
194                     } else if (isDeleteCmd()) {
195                         clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
196                     }
197                 }
198
199                 @Override
200                 public void onFailure() {
201                     clone.onFailure(transaction);
202                 }
203
204                 @Override
205                 public void onSuccess() {
206                     clone.onSuccess(transaction);
207                 }
208             };
209             LOG.info("Update Adding to op wait queue for key: {} txId: {}", getNodeKeyStr(key), transactionId);
210             addJobToQueue(opWaitingJob);
211             return;
212         }
213     }
214
215     @Override
216     public void doDeviceTransaction(final TransactionBuilder transaction, final InstanceIdentifier<Node> nodeIid,
217             final T data, final InstanceIdentifier key, final Object... extraData) {
218         //tobe removed as part of refactoring patch
219     }
220
221     @Override
222     public void onConfigUpdate(final TransactionBuilder transaction, final InstanceIdentifier<Node> nodeIid,
223             final T data, final InstanceIdentifier key, final Object... extraData) {
224         //tobe removed as part of refactoring patch
225     }
226
227     protected A augmentation(final Node node) {
228         if (node == null) {
229             return null;
230         }
231         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
232         Class<? extends Augmentation<Node>> augType =
233                 (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
234         Augmentation<Node> augmentation = node.augmentation(augType);
235         return (A) augmentation;
236     }
237
238     protected Map<I, T> getData(final A augmentation) {
239         return Collections.emptyMap();
240     }
241
242     protected List<T> getData(final Node node) {
243         A augmentation = augmentation(node);
244         if (augmentation != null) {
245             Map<I, T> data = getData(augmentation);
246             if (data != null) {
247                 // TODO: why are we performing a copy here?
248                 return new ArrayList<>(data.values());
249             }
250         }
251         return Collections.emptyList();
252     }
253
254     @NonNull
255     protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
256             final Collection<DataTreeModification<Node>> modification, final Class<T> class1) {
257         Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
258         if (modification != null && !modification.isEmpty()) {
259             for (DataTreeModification<Node> change : modification) {
260                 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
261                 if (!Objects.equals(hwvtepOperationalState.getConnectionInstance().getInstanceIdentifier(), key)) {
262                     continue;
263                 }
264                 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
265                 List<T> removed;
266                 if (getOperationalState().isInReconciliation()) {
267                     removed = getRemoved(change);
268                 } else {
269                     removed = (List<T>) getOperationalState().getDeletedData(key, classType);
270                 }
271                 removed.addAll(getCascadeDeleteData(change));
272                 result.put(key, removed);
273             }
274         }
275         return result;
276     }
277
278     @NonNull
279     protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
280             final Collection<DataTreeModification<Node>> modification, final Class<T> class1) {
281         Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
282         if (modification != null && !modification.isEmpty()) {
283             for (DataTreeModification<Node> change : modification) {
284                 InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
285                 if (!Objects.equals(hwvtepOperationalState.getConnectionInstance().getInstanceIdentifier(), key)) {
286                     continue;
287                 }
288                 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
289                 List<T> updated = null;
290                 if (getOperationalState().isInReconciliation()) {
291                     updated = getUpdated(change);
292                 } else {
293                     updated = (List<T>) getOperationalState().getUpdatedData(key, classType);
294                 }
295                 result.put(key, updated);
296             }
297         }
298         return result;
299     }
300
301     List<T> getCascadeDeleteData(final DataTreeModification<Node> change) {
302         if (!cascadeDelete()) {
303             return Collections.emptyList();
304         }
305         DataObjectModification<Node> mod = change.getRootNode();
306         Node updatedNode = TransactUtils.getUpdated(mod);
307         List<T> updatedData = getData(updatedNode);
308         Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
309         UnMetDependencyGetter dependencyGetter = getDependencyGetter();
310         if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData)
311                 && dependencyGetter != null) {
312             List<T> removed = new ArrayList<>();
313             for (T ele : updatedData) {
314                 if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
315                     removed.add(ele);
316                 }
317             }
318             return removed;
319         }
320         return Collections.emptyList();
321     }
322
323     List<T> getRemoved(final DataTreeModification<Node> change) {
324         DataObjectModification<Node> mod = change.getRootNode();
325
326         Node removed = TransactUtils.getRemoved(mod);
327         Node updated = TransactUtils.getUpdated(mod);
328         Node before = mod.getDataBefore();
329         return diffOf(removed, before, updated, true);
330     }
331
332     List<T> getUpdated(final DataTreeModification<Node> change) {
333         DataObjectModification<Node> mod = change.getRootNode();
334         Node updated = TransactUtils.getUpdated(mod);
335         Node before = mod.getDataBefore();
336         return diffOf(updated, before, false);
337     }
338
339     List<T> diffOf(final Node include, final Node node1, final Node node2, final boolean compareKeyOnly) {
340         List<T> data1 = getData(include);
341         List<T> data2 = diffOf(node1, node2, compareKeyOnly);
342         if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
343             return Collections.emptyList();
344         }
345         List<T> result = new ArrayList<>(data1);
346         result.addAll(data2);
347         return result;
348     }
349
350     List<T> diffOf(final Node node1, final Node node2, final boolean compareKeyOnly) {
351         List<T> result = new ArrayList<>();
352
353         List<T> list1 = getData(node1);
354         List<T> list2 = getData(node2);
355
356         if (HwvtepSouthboundUtil.isEmpty(list1)) {
357             return Collections.emptyList();
358         }
359         if (HwvtepSouthboundUtil.isEmpty(list2)) {
360             return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.emptyList() : list1;
361         }
362
363         Map<Object, T> map1 = list1.stream().collect(Collectors.toMap(Identifiable::key, ele -> ele));
364         Map<Object, T> map2 = list2.stream().collect(Collectors.toMap(Identifiable::key, ele -> ele));
365         map1.entrySet().forEach(entry1 -> {
366             T val2 = map2.remove(entry1.getKey());
367             if (compareKeyOnly) {
368                 if (val2 == null) {
369                     result.add(entry1.getValue());
370                 }
371             } else {
372                 if (val2 == null) {
373                     result.add(entry1.getValue());
374                     return;
375                 }
376                 if (!areEqual(entry1.getValue(), val2)) {
377                     result.add(entry1.getValue());
378                 }
379             }
380         });
381         return result;
382     }
383
384
385     protected Type getClassType() {
386         Type type = getClass().getGenericSuperclass();
387         Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
388         return classType;
389     }
390
391     protected boolean areEqual(final T obj1, final T obj2) {
392         return obj1.key().equals(obj2.key());
393     }
394
395     protected UnMetDependencyGetter<T> getDependencyGetter() {
396         return null;
397     }
398
399     /**
400      * Tells if this object needs to be deleted if its dependent object gets deleted
401      * Ex : LocalUcastMac and LocalMacstMac.
402      *
403      * @return true if this object needs to be deleted if its dependent object gets deleted
404      */
405     protected boolean cascadeDelete() {
406         return false;
407     }
408
409     protected boolean isDeleteCmd() {
410         return false;
411     }
412
413     protected HwvtepDeviceInfo getDeviceInfo() {
414         return getOperationalState().getDeviceInfo();
415     }
416
417     protected TransactionBuilder getDeviceTransaction() {
418         return deviceTransaction;
419     }
420
421     @Override
422     public void onSuccess(final TransactionBuilder deviceTx) {
423         onCommandSucceeded();
424     }
425
426     @Override
427     public void onFailure(final TransactionBuilder deviceTx) {
428         onCommandFailed();
429     }
430
431     protected void onCommandSucceeded() {
432     }
433
434     protected void onCommandFailed() {
435     }
436
437     void updateControllerTxHistory(final TransactionType transactionType, final Object element) {
438         getOperationalState().getDeviceInfo().addToControllerTx(transactionType, element);
439     }
440
441     public <T> HwvtepDeviceInfo.DeviceData fetchDeviceData(final Class<? extends Identifiable> cls,
442             final InstanceIdentifier key) {
443         HwvtepDeviceInfo.DeviceData deviceData  = getDeviceOpData(cls, key);
444         if (deviceData == null) {
445             LOG.debug("Could not find data for key {}", getNodeKeyStr(key));
446             java.util.Optional<TypedBaseTable> optional =
447                     getTableReader().getHwvtepTableEntryUUID(cls, key, null);
448             if (optional.isPresent()) {
449                 LOG.debug("Found the data for key from device {} ", getNodeKeyStr(key));
450                 getDeviceInfo().updateDeviceOperData(cls, key, optional.get().getUuid(), optional.get());
451                 return getDeviceOpData(cls, key);
452             } else {
453                 LOG.info("Could not Find the data for key from device {} ", getNodeKeyStr(key));
454             }
455         }
456         return deviceData;
457     }
458
459     public <K extends Identifiable> void addJobToQueue(final DependentJob<K> job) {
460         hwvtepOperationalState.getDeviceInfo().putKeyInDependencyQueue(job.getKey());
461         hwvtepOperationalState.getDeviceInfo().addJobToQueue(job);
462     }
463
464     public void markKeyAsInTransit(final Class<? extends Identifiable> cls, final InstanceIdentifier key) {
465         hwvtepOperationalState.getDeviceInfo().markKeyAsInTransit(cls, key);
466     }
467
468     public HwvtepDeviceInfo.DeviceData getDeviceOpData(final Class<? extends Identifiable> cls,
469             final InstanceIdentifier key) {
470         return getOperationalState().getDeviceInfo().getDeviceOperData(cls, key);
471     }
472
473     public void clearConfigData(final Class<? extends Identifiable> cls, final InstanceIdentifier key) {
474         hwvtepOperationalState.getDeviceInfo().clearConfigData(cls, key);
475     }
476
477     public HwvtepDeviceInfo.DeviceData getConfigData(final Class<? extends Identifiable> cls,
478             final InstanceIdentifier key) {
479         return hwvtepOperationalState.getDeviceInfo().getConfigData(cls, key);
480     }
481
482     public void updateConfigData(final Class<? extends Identifiable> cls, final InstanceIdentifier key,
483             final Object data) {
484         hwvtepOperationalState.getDeviceInfo().updateConfigData(cls, key, data);
485     }
486
487     @SuppressWarnings("checkstyle:IllegalCatch")
488     public AbstractTransactCommand getClone() {
489         try {
490             return getClass().getConstructor(HwvtepOperationalState.class, Collection.class)
491                     .newInstance(hwvtepOperationalState, changes);
492         } catch (Throwable e) {
493             LOG.error("Failed to clone the cmd ", e);
494         }
495         return this;
496     }
497
498     public HwvtepTableReader getTableReader() {
499         return getOperationalState().getConnectionInstance().getHwvtepTableReader();
500     }
501
502     public HwvtepConnectionInstance getConnectionInstance() {
503         return hwvtepOperationalState.getConnectionInstance();
504     }
505
506     public HwvtepOperationalState newOperState() {
507         return new HwvtepOperationalState(getConnectionInstance());
508     }
509
510     protected String getNodeKeyStr(final InstanceIdentifier<T> iid) {
511         return getClassType().getTypeName() + "."
512             + iid.firstKeyOf(Node.class).getNodeId().getValue() + "." + getKeyStr(iid);
513     }
514
515     protected String getKeyStr(final InstanceIdentifier<T> iid) {
516         return iid.toString();
517     }
518
519     protected String getLsKeyStr(final InstanceIdentifier iid) {
520         return ((InstanceIdentifier<LogicalSwitches>)iid).firstKeyOf(LogicalSwitches.class)
521             .getHwvtepNodeName().getValue();
522     }
523 }