32c07889ffeb4d18493e1501ba85c10399745b1d
[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().updateConfigData(cls, key, data);
66     }
67
68     void processDependencies(final UnMetDependencyGetter<T> unMetDependencyGetter,
69                              final TransactionBuilder transaction,
70                              final InstanceIdentifier<Node> nodeIid,
71                              final InstanceIdentifier key,
72                              final T data, final Object... extraData) {
73
74         HwvtepDeviceInfo deviceInfo = operationalState.getDeviceInfo();
75         Map inTransitDependencies = Collections.EMPTY_MAP;
76         Map confingDependencies = Collections.EMPTY_MAP;
77
78         if (!isRemoveCommand() && unMetDependencyGetter != null) {
79             inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(operationalState, data);
80             confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(operationalState, data);
81             //we can skip the config termination point dependency as we can create them in device as part of this tx
82             confingDependencies.remove(TerminationPoint.class);
83         }
84
85         Type type = getClass().getGenericSuperclass();
86         Type classType = ((ParameterizedType)type).getActualTypeArguments()[0];
87
88         //If this key itself is in transit wait for the response of this key itself
89         if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
90             inTransitDependencies.put(classType, Collections.singletonList(key));
91         }
92
93         if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies) && HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
94             doDeviceTransaction(transaction, nodeIid, data, key, extraData);
95             updateCurrentTxData((Class<? extends Identifiable>) classType, key, new UUID("uuid"), data);
96         }
97         if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
98             DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
99                     key, data, confingDependencies) {
100
101                 @Override
102                 public void onDependencyResolved(HwvtepOperationalState operationalState,
103                                                  TransactionBuilder transactionBuilder) {
104                     AbstractTransactCommand.this.operationalState = operationalState;
105                     onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
106                 }
107             };
108             deviceInfo.addJobToQueue(configWaitingJob);
109         }
110         if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
111
112             DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
113                     key, data, inTransitDependencies) {
114
115                 @Override
116                 public void onDependencyResolved(HwvtepOperationalState operationalState,
117                                                  TransactionBuilder transactionBuilder) {
118                     AbstractTransactCommand.this.operationalState = operationalState;
119                     onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
120                 }
121             };
122             deviceInfo.addJobToQueue(opWaitingJob);
123         }
124     }
125
126     public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
127                                     InstanceIdentifier key, Object... extraData) {
128         //tobe removed as part of refactoring patch
129     }
130
131     public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
132                                InstanceIdentifier key, Object... extraData) {
133         //tobe removed as part of refactoring patch
134     }
135
136     protected Aug getAugmentation(Node node) {
137         if (node == null) {
138             return null;
139         }
140         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
141         Class<? extends Augmentation<Node>> augType = (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
142         Augmentation<Node> augmentation = node.getAugmentation(augType);
143         return (Aug)augmentation;
144     }
145
146     protected List<T> getData(Aug augmentation) {
147         return Collections.EMPTY_LIST;
148     }
149
150     protected List<T> getData(Node node) {
151         Aug augmentation = getAugmentation(node);
152         if (augmentation != null) {
153             List<T> data = getData(augmentation);
154             if (data != null) {
155                 return new ArrayList<>(data);
156             }
157         }
158         return Collections.emptyList();
159     }
160
161     protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
162             Collection<DataTreeModification<Node>> changes, Class<T> class1) {
163         Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
164         if (changes != null && !changes.isEmpty()) {
165             for (DataTreeModification<Node> change : changes) {
166                 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
167                 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
168                 List<T> removed;
169                 if (operationalState.isInReconciliation()) {
170                     removed = getRemoved(change);
171                 } else {
172                     removed = (List<T>) operationalState.getDeletedData(key, classType);
173                 }
174                 removed.addAll(getCascadeDeleteData(change));
175                 result.put(key, removed);
176             }
177         }
178         return result;
179     }
180
181     protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
182             Collection<DataTreeModification<Node>> changes, Class<T> class1) {
183         Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
184         if (changes != null && !changes.isEmpty()) {
185             for (DataTreeModification<Node> change : changes) {
186                 InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
187                 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
188                 List<T> updated = null;
189                 if (operationalState.isInReconciliation()) {
190                     updated = getUpdated(change);
191                 } else {
192                     updated = (List<T>) operationalState.getUpdatedData(key, classType);
193                 }
194                 result.put(key, updated);
195             }
196         }
197         return result;
198     }
199
200     List<T>  getCascadeDeleteData(DataTreeModification<Node> change) {
201         if (!cascadeDelete()) {
202             return Collections.EMPTY_LIST;
203         }
204         DataObjectModification<Node> mod = change.getRootNode();
205         Node updatedNode = TransactUtils.getUpdated(mod);
206         List<T> updatedData = getData(updatedNode);
207         Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
208         UnMetDependencyGetter dependencyGetter = getDependencyGetter();
209         if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData) && dependencyGetter != null) {
210             List<T> removed = new ArrayList<>();
211             for (T ele : updatedData) {
212                 if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
213                     removed.add(ele);
214                 }
215             }
216             return removed;
217         }
218         return Collections.EMPTY_LIST;
219     }
220
221     List<T> getRemoved(DataTreeModification<Node> change) {
222         DataObjectModification<Node> mod = change.getRootNode();
223
224         Node removed = TransactUtils.getRemoved(mod);
225         Node updated = TransactUtils.getUpdated(mod);
226         Node before = mod.getDataBefore();
227         return diffOf(removed, before, updated, true);
228     }
229
230     List<T> getUpdated(DataTreeModification<Node> change) {
231         DataObjectModification<Node> mod = change.getRootNode();
232         Node updated = TransactUtils.getUpdated(mod);
233         Node before = mod.getDataBefore();
234         return diffOf(updated, before, false);
235     }
236
237     List<T> diffOf(Node include, Node a, Node b, boolean compareKeyOnly) {
238         List<T> data1 = getData(include);
239         List<T> data2 = diffOf(a, b, compareKeyOnly);
240         if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
241             return Collections.emptyList();
242         }
243         List<T> result = new ArrayList<>(data1);
244         result.addAll(data2);
245         return result;
246     }
247
248     List<T> diffOf(Node a, Node b, boolean compareKeyOnly) {
249         List<T> result = new ArrayList<>();
250
251         List<T> list1 = getData(a);
252         List<T> list2 = getData(b);
253
254         if (HwvtepSouthboundUtil.isEmpty(list1)) {
255             return Collections.emptyList();
256         }
257         if (HwvtepSouthboundUtil.isEmpty(list2)) {
258             return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.emptyList() : list1;
259         }
260
261         Iterator<T> it1 = list1.iterator();
262
263         while(it1.hasNext()) {
264             T ele = it1.next();
265             Iterator<T> it2 = list2.iterator();
266             boolean found = false;
267             while (it2.hasNext()) {
268                 T other  = it2.next();
269                 found = compareKeyOnly ? Objects.equals(ele.getKey(), other.getKey()) : areEqual(ele, other);
270                 if ( found ) {
271                     it2.remove();
272                     break;
273                 }
274             }
275             if (!found) {
276                 result.add(ele);
277             }
278         }
279         return result;
280     }
281
282
283     protected Type getClassType() {
284         Type type = getClass().getGenericSuperclass();
285         Type classType = ((ParameterizedType)type).getActualTypeArguments()[0];
286         return classType;
287     }
288
289     protected boolean areEqual(T a , T b) {
290         return a.getKey().equals(b.getKey());
291     }
292
293     protected UnMetDependencyGetter getDependencyGetter() {
294         return null;
295     }
296
297     /**
298      * Tells if this object needs to be deleted if its dependent object gets deleted
299      * Ex : LocalUcastMac and LocalMacstMac
300      * @return true if this object needs to be deleted if its dependent object gets deleted
301      */
302     protected boolean cascadeDelete() {
303         return false;
304     }
305
306     protected boolean isRemoveCommand() {
307         return false;
308     }
309 }