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