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