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