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