2688a20003946007766fa51ac13d13cf7684318d
[ovsdb.git] / hwvtepsouthbound / hwvtepsouthbound-impl / src / main / java / org / opendaylight / ovsdb / hwvtepsouthbound / transact / AbstractTransactCommand.java
1 /*
2  * Copyright © 2015, 2017 China Telecom Beijing Research Institute and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
10
11 import java.lang.reflect.ParameterizedType;
12 import java.lang.reflect.Type;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Objects;
21 import java.util.Set;
22
23 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
25 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
26 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil;
27 import org.opendaylight.ovsdb.lib.notation.UUID;
28 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
30 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
32 import org.opendaylight.yangtools.yang.binding.Augmentation;
33 import org.opendaylight.yangtools.yang.binding.Identifiable;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35
36 public abstract class AbstractTransactCommand<T extends Identifiable, Aug extends Augmentation<Node>> implements TransactCommand<T> {
37
38     private HwvtepOperationalState operationalState;
39     private Collection<DataTreeModification<Node>> changes;
40
41     protected AbstractTransactCommand() {
42         // NO OP
43     }
44
45     public AbstractTransactCommand(HwvtepOperationalState state, Collection<DataTreeModification<Node>> changes) {
46         this.operationalState = state;
47         this.changes = changes;
48     }
49
50     public HwvtepOperationalState getOperationalState() {
51         return operationalState;
52     }
53
54     public Collection<DataTreeModification<Node>> getChanges() {
55         return changes;
56     }
57
58     void updateCurrentTxDeleteData(Class<? extends Identifiable> cls, InstanceIdentifier key, T data) {
59         operationalState.updateCurrentTxDeleteData(cls, key);
60         operationalState.getDeviceInfo().clearConfigData(cls, key);
61     }
62
63     void updateCurrentTxData(Class<? extends Identifiable> cls, InstanceIdentifier key, UUID uuid, Object data) {
64         operationalState.updateCurrentTxData(cls, key, uuid);
65         operationalState.getDeviceInfo().markKeyAsInTransit(cls, key);
66         operationalState.getDeviceInfo().updateConfigData(cls, key, data);
67     }
68
69     void processDependencies(UnMetDependencyGetter<T> unMetDependencyGetter,
70                              TransactionBuilder transaction,
71                              final InstanceIdentifier<Node> nodeIid,
72                              final InstanceIdentifier key,
73                              final T data, final Object... extraData) {
74
75         HwvtepDeviceInfo deviceInfo = operationalState.getDeviceInfo();
76         Map inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(operationalState, data);
77         Map confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(operationalState, data);
78         //we can skip the config termination point dependency as we can create them in device as part of this tx
79         confingDependencies.remove(TerminationPoint.class);
80
81         Type type = getClass().getGenericSuperclass();
82         Type classType = ((ParameterizedType)type).getActualTypeArguments()[0];
83
84         //If this key itself is in transit wait for the response of this key itself
85         if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
86             inTransitDependencies.put(classType, Collections.singletonList(key));
87         }
88
89         if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies) && HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
90             doDeviceTransaction(transaction, nodeIid, data, key, extraData);
91             //TODO put proper uuid
92             updateCurrentTxData((Class<? extends Identifiable>) classType, key, new UUID("uuid"), data);
93         }
94         if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
95             DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
96                     key, data, confingDependencies) {
97
98                 @Override
99                 public void onDependencyResolved(HwvtepOperationalState operationalState,
100                                                  TransactionBuilder transactionBuilder) {
101                     AbstractTransactCommand.this.operationalState = operationalState;
102                     onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
103                 }
104             };
105             deviceInfo.addJobToQueue(configWaitingJob);
106         }
107         if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
108
109             DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
110                     key, data, inTransitDependencies) {
111
112                 @Override
113                 public void onDependencyResolved(HwvtepOperationalState operationalState,
114                                                  TransactionBuilder transactionBuilder) {
115                     AbstractTransactCommand.this.operationalState = operationalState;
116                     onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
117                 }
118             };
119             deviceInfo.addJobToQueue(opWaitingJob);
120         }
121     }
122
123     public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
124                                     InstanceIdentifier key, Object... extraData) {
125         //tobe removed as part of refactoring patch
126     }
127
128     public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
129                                InstanceIdentifier key, Object... extraData) {
130         //tobe removed as part of refactoring patch
131     }
132
133     protected Aug getAugmentation(Node node) {
134         if (node == null) {
135             return null;
136         }
137         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
138         Class<? extends Augmentation<Node>> augType = (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
139         Augmentation<Node> augmentation = node.getAugmentation(augType);
140         return (Aug)augmentation;
141     }
142
143     protected List<T> getData(Aug augmentation) {
144         return Collections.EMPTY_LIST;
145     }
146
147     protected List<T> getData(Node node) {
148         Aug augmentation = getAugmentation(node);
149         if (augmentation != null) {
150             List<T> data = getData(augmentation);
151             if (data != null) {
152                 return new ArrayList<>(data);
153             }
154         }
155         return Collections.emptyList();
156     }
157
158     protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
159             Collection<DataTreeModification<Node>> changes, Class<T> class1) {
160         Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
161         if (changes != null && !changes.isEmpty()) {
162             for (DataTreeModification<Node> change : changes) {
163                 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
164                 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
165                 List<T> removed;
166                 if (operationalState.isInReconciliation()) {
167                     removed = getRemoved(change);
168                 } else {
169                     removed = (List<T>) operationalState.getDeletedData(key, classType);
170                 }
171                 removed.addAll(getCascadeDeleteData(change));
172                 result.put(key, removed);
173             }
174         }
175         return result;
176     }
177
178     protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
179             Collection<DataTreeModification<Node>> changes, Class<T> class1) {
180         Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
181         if (changes != null && !changes.isEmpty()) {
182             for (DataTreeModification<Node> change : changes) {
183                 InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
184                 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
185                 List<T> updated = null;
186                 if (operationalState.isInReconciliation()) {
187                     updated = getUpdated(change);
188                 } else {
189                     updated = (List<T>) operationalState.getUpdatedData(key, classType);
190                 }
191                 result.put(key, updated);
192             }
193         }
194         return result;
195     }
196
197     List<T>  getCascadeDeleteData(DataTreeModification<Node> change) {
198         if (!cascadeDelete()) {
199             return Collections.EMPTY_LIST;
200         }
201         DataObjectModification<Node> mod = change.getRootNode();
202         Node updatedNode = TransactUtils.getUpdated(mod);
203         List<T> updatedData = getData(updatedNode);
204         Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
205         UnMetDependencyGetter dependencyGetter = getDependencyGetter();
206         if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData) && dependencyGetter != null) {
207             List<T> removed = new ArrayList<>();
208             for (T ele : updatedData) {
209                 if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
210                     removed.add(ele);
211                 }
212             }
213             return removed;
214         }
215         return Collections.EMPTY_LIST;
216     }
217
218     List<T> getRemoved(DataTreeModification<Node> change) {
219         DataObjectModification<Node> mod = change.getRootNode();
220
221         Node removed = TransactUtils.getRemoved(mod);
222         Node updated = TransactUtils.getUpdated(mod);
223         Node before = mod.getDataBefore();
224         return diffOf(removed, before, updated, true);
225     }
226
227     List<T> getUpdated(DataTreeModification<Node> change) {
228         DataObjectModification<Node> mod = change.getRootNode();
229         Node updated = TransactUtils.getUpdated(mod);
230         Node before = mod.getDataBefore();
231         return diffOf(updated, before, false);
232     }
233
234     List<T> diffOf(Node include, Node a, Node b, boolean compareKeyOnly) {
235         List<T> data1 = getData(include);
236         List<T> data2 = diffOf(a, b, compareKeyOnly);
237         if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
238             return Collections.emptyList();
239         }
240         List<T> result = new ArrayList<>(data1);
241         result.addAll(data2);
242         return result;
243     }
244
245     List<T> diffOf(Node a, Node b, boolean compareKeyOnly) {
246         List<T> result = new ArrayList<>();
247
248         List<T> list1 = getData(a);
249         List<T> list2 = getData(b);
250
251         if (HwvtepSouthboundUtil.isEmpty(list1)) {
252             return Collections.emptyList();
253         }
254         if (HwvtepSouthboundUtil.isEmpty(list2)) {
255             return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.emptyList() : list1;
256         }
257
258         Iterator<T> it1 = list1.iterator();
259
260         while(it1.hasNext()) {
261             T ele = it1.next();
262             Iterator<T> it2 = list2.iterator();
263             boolean found = false;
264             while (it2.hasNext()) {
265                 T other  = it2.next();
266                 found = compareKeyOnly ? Objects.equals(ele.getKey(), other.getKey()) : areEqual(ele, other);
267                 if ( found ) {
268                     it2.remove();
269                     break;
270                 }
271             }
272             if (!found) {
273                 result.add(ele);
274             }
275         }
276         return result;
277     }
278
279
280     protected Type getClassType() {
281         Type type = getClass().getGenericSuperclass();
282         Type classType = ((ParameterizedType)type).getActualTypeArguments()[0];
283         return classType;
284     }
285
286     protected boolean areEqual(T a , T b) {
287         return a.getKey().equals(b.getKey());
288     }
289
290     protected UnMetDependencyGetter getDependencyGetter() {
291         return null;
292     }
293
294     /**
295      * Tells if this object needs to be deleted if its dependent object gets deleted
296      * Ex : LocalUcastMac and LocalMacstMac
297      * @return true if this object needs to be deleted if its dependent object gets deleted
298      */
299     protected boolean cascadeDelete() {
300         return false;
301     }
302 }