2 * Copyright © 2015, 2017 China Telecom Beijing Research Institute and others. All rights reserved.
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
9 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
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;
20 import java.util.Objects;
22 import java.util.concurrent.ConcurrentHashMap;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
29 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil;
30 import org.opendaylight.ovsdb.lib.notation.UUID;
31 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
32 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
33 import org.opendaylight.ovsdb.utils.mdsal.utils.TransactionType;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
37 import org.opendaylight.yangtools.yang.binding.Augmentation;
38 import org.opendaylight.yangtools.yang.binding.Identifiable;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 public abstract class AbstractTransactCommand<T extends Identifiable, A extends Augmentation<Node>>
44 implements TransactCommand<T> {
46 private static final Logger LOG = LoggerFactory.getLogger(AbstractTransactCommand.class);
47 protected static final UUID TXUUID = new UUID("TXUUID");
48 protected ThreadLocal<HwvtepOperationalState> threadLocalOperationalState = new ThreadLocal<>();
49 protected ThreadLocal<TransactionBuilder> threadLocalDeviceTransaction = new ThreadLocal<>();
50 private Collection<DataTreeModification<Node>> changes;
51 protected Map<TransactionBuilder, List<MdsalUpdate<T>>> updates = new ConcurrentHashMap<>();
53 protected AbstractTransactCommand() {
57 public AbstractTransactCommand(HwvtepOperationalState state, Collection<DataTreeModification<Node>> changes) {
58 this.threadLocalOperationalState.set(state);
59 this.changes = changes;
62 public HwvtepOperationalState getOperationalState() {
63 return threadLocalOperationalState.get();
66 public DataBroker getDataBroker() {
67 return getOperationalState().getDataBroker();
70 public Collection<DataTreeModification<Node>> getChanges() {
74 void updateCurrentTxDeleteData(Class<? extends Identifiable> cls, InstanceIdentifier key, T data) {
75 getOperationalState().getDeviceInfo().markKeyAsInTransit(cls, key);
76 addToUpdates(key, data);
77 getOperationalState().getDeviceInfo().clearConfigData(cls, key);
80 void updateCurrentTxData(Class<? extends Identifiable> cls, InstanceIdentifier key, UUID uuid, T data) {
81 getOperationalState().getDeviceInfo().markKeyAsInTransit(cls, key);
82 addToUpdates(key, data);
83 getOperationalState().getDeviceInfo().updateConfigData(cls, key, data);
86 void addToUpdates(InstanceIdentifier key, T data) {
88 Type type = getClass().getGenericSuperclass();
89 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
90 if (getDeviceInfo().getConfigData((Class<? extends Identifiable>) classType, key) != null) {
91 oldData = (T) getDeviceInfo().getConfigData((Class<? extends Identifiable>) classType, key);
93 updates.putIfAbsent(getDeviceTransaction(), new ArrayList<MdsalUpdate<T>>());
94 updates.get(getDeviceTransaction()).add(new MdsalUpdate<T>(key, data, oldData));
97 void processDependencies(final UnMetDependencyGetter<T> unMetDependencyGetter,
98 final TransactionBuilder transaction,
99 final InstanceIdentifier<Node> nodeIid,
100 final InstanceIdentifier key,
101 final T data, final Object... extraData) {
103 this.threadLocalDeviceTransaction.set(transaction);
104 HwvtepDeviceInfo deviceInfo = getOperationalState().getDeviceInfo();
105 Map inTransitDependencies = new HashMap<>();
106 Map confingDependencies = new HashMap<>();
108 if (!isRemoveCommand() && unMetDependencyGetter != null) {
109 inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(getOperationalState(), data);
110 confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(getOperationalState(), data);
111 //we can skip the config termination point dependency as we can create them in device as part of this tx
112 confingDependencies.remove(TerminationPoint.class);
115 Type type = getClass().getGenericSuperclass();
116 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
118 //If this key itself is in transit wait for the response of this key itself
119 if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
120 inTransitDependencies.put(classType, Collections.singletonList(key));
123 if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies) && HwvtepSouthboundUtil.isEmptyMap(
124 inTransitDependencies)) {
125 doDeviceTransaction(transaction, nodeIid, data, key, extraData);
126 if (isRemoveCommand()) {
127 getDeviceInfo().clearConfigData((Class<? extends Identifiable>) classType, key);
129 getDeviceInfo().updateConfigData((Class<? extends Identifiable>) classType, key, data);
132 if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
133 DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
134 key, data, confingDependencies) {
137 public void onDependencyResolved(HwvtepOperationalState operationalState,
138 TransactionBuilder transactionBuilder) {
139 AbstractTransactCommand.this.threadLocalOperationalState.set(operationalState);
140 AbstractTransactCommand.this.threadLocalDeviceTransaction.set(transactionBuilder);
141 onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
144 public void onFailure() {
145 AbstractTransactCommand.this.onFailure(getDeviceTransaction());
148 public void onSuccess() {
149 AbstractTransactCommand.this.onSuccess(getDeviceTransaction());
152 deviceInfo.addJobToQueue(configWaitingJob);
154 if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
156 DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
157 key, data, inTransitDependencies) {
160 public void onDependencyResolved(HwvtepOperationalState operationalState,
161 TransactionBuilder transactionBuilder) {
162 //data would have got deleted by , push the data only if it is still in configds
163 threadLocalOperationalState.set(operationalState);
164 threadLocalDeviceTransaction.set(transactionBuilder);
165 T data = (T) new MdsalUtils(operationalState.getDataBroker()).read(
166 LogicalDatastoreType.CONFIGURATION, key);
168 onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
170 LOG.warn("Skipping add of key: {} as it is not present txId: {}", key);
174 public void onFailure() {
175 AbstractTransactCommand.this.onFailure(getDeviceTransaction());
178 public void onSuccess() {
179 AbstractTransactCommand.this.onSuccess(getDeviceTransaction());
182 deviceInfo.addJobToQueue(opWaitingJob);
186 public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
187 InstanceIdentifier key, Object... extraData) {
188 //tobe removed as part of refactoring patch
191 public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
192 InstanceIdentifier key, Object... extraData) {
193 //tobe removed as part of refactoring patch
196 protected A getAugmentation(Node node) {
200 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
201 Class<? extends Augmentation<Node>> augType =
202 (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
203 Augmentation<Node> augmentation = node.getAugmentation(augType);
204 return (A) augmentation;
207 protected List<T> getData(A augmentation) {
208 return Collections.EMPTY_LIST;
211 protected List<T> getData(Node node) {
212 A augmentation = getAugmentation(node);
213 if (augmentation != null) {
214 List<T> data = getData(augmentation);
216 return new ArrayList<>(data);
219 return Collections.emptyList();
222 protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
223 Collection<DataTreeModification<Node>> changes, Class<T> class1) {
224 Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
225 if (changes != null && !changes.isEmpty()) {
226 for (DataTreeModification<Node> change : changes) {
227 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
228 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
230 if (getOperationalState().isInReconciliation()) {
231 removed = getRemoved(change);
233 removed = (List<T>) getOperationalState().getDeletedData(key, classType);
235 removed.addAll(getCascadeDeleteData(change));
236 result.put(key, removed);
242 protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
243 Collection<DataTreeModification<Node>> changes, Class<T> class1) {
244 Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
245 if (changes != null && !changes.isEmpty()) {
246 for (DataTreeModification<Node> change : changes) {
247 InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
248 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
249 List<T> updated = null;
250 if (getOperationalState().isInReconciliation()) {
251 updated = getUpdated(change);
253 updated = (List<T>) getOperationalState().getUpdatedData(key, classType);
255 result.put(key, updated);
261 List<T> getCascadeDeleteData(DataTreeModification<Node> change) {
262 if (!cascadeDelete()) {
263 return Collections.EMPTY_LIST;
265 DataObjectModification<Node> mod = change.getRootNode();
266 Node updatedNode = TransactUtils.getUpdated(mod);
267 List<T> updatedData = getData(updatedNode);
268 Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
269 UnMetDependencyGetter dependencyGetter = getDependencyGetter();
270 if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData)
271 && dependencyGetter != null) {
272 List<T> removed = new ArrayList<>();
273 for (T ele : updatedData) {
274 if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
280 return Collections.EMPTY_LIST;
283 List<T> getRemoved(DataTreeModification<Node> change) {
284 DataObjectModification<Node> mod = change.getRootNode();
286 Node removed = TransactUtils.getRemoved(mod);
287 Node updated = TransactUtils.getUpdated(mod);
288 Node before = mod.getDataBefore();
289 return diffOf(removed, before, updated, true);
292 List<T> getUpdated(DataTreeModification<Node> change) {
293 DataObjectModification<Node> mod = change.getRootNode();
294 Node updated = TransactUtils.getUpdated(mod);
295 Node before = mod.getDataBefore();
296 return diffOf(updated, before, false);
299 List<T> diffOf(Node include, Node a, Node b, boolean compareKeyOnly) {
300 List<T> data1 = getData(include);
301 List<T> data2 = diffOf(a, b, compareKeyOnly);
302 if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
303 return Collections.emptyList();
305 List<T> result = new ArrayList<>(data1);
306 result.addAll(data2);
310 List<T> diffOf(Node a, Node b, boolean compareKeyOnly) {
311 List<T> result = new ArrayList<>();
313 List<T> list1 = getData(a);
314 List<T> list2 = getData(b);
316 if (HwvtepSouthboundUtil.isEmpty(list1)) {
317 return Collections.emptyList();
319 if (HwvtepSouthboundUtil.isEmpty(list2)) {
320 return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.emptyList() : list1;
323 Iterator<T> it1 = list1.iterator();
325 while (it1.hasNext()) {
327 Iterator<T> it2 = list2.iterator();
328 boolean found = false;
329 while (it2.hasNext()) {
330 T other = it2.next();
331 found = compareKeyOnly ? Objects.equals(ele.getKey(), other.getKey()) : areEqual(ele, other);
345 protected Type getClassType() {
346 Type type = getClass().getGenericSuperclass();
347 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
351 protected boolean areEqual(T a, T b) {
352 return a.getKey().equals(b.getKey());
355 protected UnMetDependencyGetter getDependencyGetter() {
360 * Tells if this object needs to be deleted if its dependent object gets deleted
361 * Ex : LocalUcastMac and LocalMacstMac
363 * @return true if this object needs to be deleted if its dependent object gets deleted
365 protected boolean cascadeDelete() {
369 protected boolean isRemoveCommand() {
373 protected HwvtepDeviceInfo getDeviceInfo() {
374 return getOperationalState().getDeviceInfo();
377 protected TransactionBuilder getDeviceTransaction() {
378 return threadLocalDeviceTransaction.get();
381 public void onSuccess(TransactionBuilder deviceTransaction) {
382 if (deviceTransaction == null || !updates.containsKey(deviceTransaction)) {
385 onCommandSucceeded();
388 public void onFailure(TransactionBuilder deviceTransaction) {
389 if (deviceTransaction == null || !updates.containsKey(deviceTransaction)) {
392 for (MdsalUpdate mdsalUpdate : updates.get(deviceTransaction)) {
393 getDeviceInfo().clearInTransit((Class<? extends Identifiable>) mdsalUpdate.getClass(),
394 mdsalUpdate.getKey());
399 protected void onCommandSucceeded() {
402 protected void onCommandFailed() {
405 void updateControllerTxHistory(TransactionType transactionType, Object element) {
406 getOperationalState().getDeviceInfo().addToControllerTx(transactionType, element);