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 com.google.common.collect.Lists;
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;
22 import java.util.Objects;
24 import java.util.stream.Collectors;
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;
47 public abstract class AbstractTransactCommand<T extends Identifiable, A extends Augmentation<Node>>
48 implements TransactCommand<T> {
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<>();
57 protected AbstractTransactCommand() {
61 public AbstractTransactCommand(HwvtepOperationalState state, Collection<DataTreeModification<Node>> changes) {
62 this.hwvtepOperationalState = state;
63 this.changes = changes;
66 public HwvtepOperationalState getOperationalState() {
67 return hwvtepOperationalState;
70 public DataBroker getDataBroker() {
71 return getOperationalState().getDataBroker();
74 public Collection<DataTreeModification<Node>> getChanges() {
78 void updateCurrentTxDeleteData(Class<? extends Identifiable> cls, InstanceIdentifier key, T data) {
79 hwvtepOperationalState.updateCurrentTxDeleteData(cls, key);
80 markKeyAsInTransit(cls, key);
81 addToUpdates(key, data);
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);
90 void addToUpdates(InstanceIdentifier key, T data) {
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();
97 updates.add(new MdsalUpdate<T>(key, data, oldData));
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) {
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();
113 if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
114 inTransitDependencies = new HashMap<>();
115 inTransitDependencies.put((Class<? extends Identifiable>) classType, Lists.newArrayList(key));
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);
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));
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);
134 getDeviceInfo().clearConfigData((Class<? extends Identifiable>) classType, key);
136 getDeviceInfo().updateConfigData((Class<? extends Identifiable>) classType, key, data);
140 if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
141 DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
142 key, data, confingDependencies) {
143 AbstractTransactCommand clone = getClone();
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);
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);
161 public void onFailure() {
162 clone.onFailure(transaction);
166 public void onSuccess() {
167 clone.onSuccess(transaction);
170 LOG.info("Update Adding to config wait queue for key: {} txId: {}",
171 key, getOperationalState().getTransactionId());
172 addJobToQueue(configWaitingJob);
175 final long transactionId = hwvtepOperationalState.getTransactionId();
176 if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
178 DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
179 key, data, inTransitDependencies, transactionId) {
180 AbstractTransactCommand clone = getClone();
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);
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);
198 public void onFailure() {
199 clone.onFailure(transaction);
203 public void onSuccess() {
204 clone.onSuccess(transaction);
207 LOG.info("Update Adding to op wait queue for key: {} txId: {}", key, transactionId);
208 addJobToQueue(opWaitingJob);
214 public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
215 InstanceIdentifier key, Object... extraData) {
216 //tobe removed as part of refactoring patch
220 public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
221 InstanceIdentifier key, Object... extraData) {
222 //tobe removed as part of refactoring patch
225 protected A augmentation(Node node) {
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;
236 protected List<T> getData(A augmentation) {
237 return Collections.emptyList();
240 protected List<T> getData(Node node) {
241 A augmentation = augmentation(node);
242 if (augmentation != null) {
243 List<T> data = getData(augmentation);
245 return new ArrayList<>(data);
248 return Collections.emptyList();
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)) {
261 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
263 if (getOperationalState().isInReconciliation()) {
264 removed = getRemoved(change);
266 removed = (List<T>) getOperationalState().getDeletedData(key, classType);
268 removed.addAll(getCascadeDeleteData(change));
269 result.put(key, removed);
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)) {
285 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
286 List<T> updated = null;
287 if (getOperationalState().isInReconciliation()) {
288 updated = getUpdated(change);
290 updated = (List<T>) getOperationalState().getUpdatedData(key, classType);
292 result.put(key, updated);
298 List<T> getCascadeDeleteData(DataTreeModification<Node> change) {
299 if (!cascadeDelete()) {
300 return Collections.emptyList();
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))) {
317 return Collections.emptyList();
320 List<T> getRemoved(DataTreeModification<Node> change) {
321 DataObjectModification<Node> mod = change.getRootNode();
323 Node removed = TransactUtils.getRemoved(mod);
324 Node updated = TransactUtils.getUpdated(mod);
325 Node before = mod.getDataBefore();
326 return diffOf(removed, before, updated, true);
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);
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;
342 List<T> result = new ArrayList<>(data1);
343 result.addAll(data2);
347 List<T> diffOf(Node node1, Node node2, boolean compareKeyOnly) {
348 List<T> result = new ArrayList<>();
350 List<T> list1 = getData(node1);
351 List<T> list2 = getData(node2);
353 if (HwvtepSouthboundUtil.isEmpty(list1)) {
354 return Collections.EMPTY_LIST;
356 if (HwvtepSouthboundUtil.isEmpty(list2)) {
357 return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.EMPTY_LIST : list1;
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) {
366 result.add(entry1.getValue());
370 result.add(entry1.getValue());
373 if (!areEqual(entry1.getValue(), val2)) {
374 result.add(entry1.getValue());
382 protected Type getClassType() {
383 Type type = getClass().getGenericSuperclass();
384 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
388 protected boolean areEqual(T obj1, T obj2) {
389 return obj1.key().equals(obj2.key());
392 protected UnMetDependencyGetter getDependencyGetter() {
397 * Tells if this object needs to be deleted if its dependent object gets deleted
398 * Ex : LocalUcastMac and LocalMacstMac.
400 * @return true if this object needs to be deleted if its dependent object gets deleted
402 protected boolean cascadeDelete() {
406 protected boolean isDeleteCmd() {
410 protected HwvtepDeviceInfo getDeviceInfo() {
411 return getOperationalState().getDeviceInfo();
414 protected TransactionBuilder getDeviceTransaction() {
415 return deviceTransaction;
419 public void onSuccess(TransactionBuilder deviceTx) {
420 onCommandSucceeded();
424 public void onFailure(TransactionBuilder deviceTx) {
428 protected void onCommandSucceeded() {
431 protected void onCommandFailed() {
434 void updateControllerTxHistory(TransactionType transactionType, Object element) {
435 getOperationalState().getDeviceInfo().addToControllerTx(transactionType, element);
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);
449 LOG.info("Could not Find the data for key from device {} ", key);
455 protected String getKeyStr(InstanceIdentifier iid) {
456 return iid.toString();
459 public <K extends Identifiable> void addJobToQueue(DependentJob<K> job) {
460 hwvtepOperationalState.getDeviceInfo().putKeyInDependencyQueue(job.getKey());
461 hwvtepOperationalState.getDeviceInfo().addJobToQueue(job);
464 public void markKeyAsInTransit(Class<? extends Identifiable> cls, InstanceIdentifier key) {
465 hwvtepOperationalState.getDeviceInfo().markKeyAsInTransit(cls, key);
468 public HwvtepDeviceInfo.DeviceData getDeviceOpData(Class<? extends Identifiable> cls, InstanceIdentifier key) {
469 return getOperationalState().getDeviceInfo().getDeviceOperData(cls, key);
472 public void clearConfigData(Class<? extends Identifiable> cls, InstanceIdentifier key) {
473 hwvtepOperationalState.getDeviceInfo().clearConfigData(cls, key);
476 public HwvtepDeviceInfo.DeviceData getConfigData(Class<? extends Identifiable> cls, InstanceIdentifier key) {
477 return hwvtepOperationalState.getDeviceInfo().getConfigData(cls, key);
480 public void updateConfigData(Class<? extends Identifiable> cls, InstanceIdentifier key, Object data) {
481 hwvtepOperationalState.getDeviceInfo().updateConfigData(cls, key, data);
484 @SuppressWarnings("checkstyle:IllegalCatch")
485 public AbstractTransactCommand getClone() {
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);
495 public HwvtepTableReader getTableReader() {
496 return getOperationalState().getConnectionInstance().getHwvtepTableReader();
499 public HwvtepConnectionInstance getConnectionInstance() {
500 return hwvtepOperationalState.getConnectionInstance();
503 public HwvtepOperationalState newOperState() {
504 return new HwvtepOperationalState(getConnectionInstance());