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
8 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
10 import com.google.common.collect.Lists;
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.List;
20 import java.util.Objects;
22 import java.util.stream.Collectors;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.binding.api.DataObjectModification;
26 import org.opendaylight.mdsal.binding.api.DataTreeModification;
27 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepConnectionInstance;
28 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
29 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil;
30 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepTableReader;
31 import org.opendaylight.ovsdb.lib.notation.UUID;
32 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
33 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
34 import org.opendaylight.ovsdb.utils.mdsal.utils.TransactionType;
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.Identifier;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
45 public abstract class AbstractTransactCommand<T extends Identifiable<I>, I extends Identifier<T>,
46 A extends Augmentation<Node>> implements TransactCommand<T> {
48 private static final Logger LOG = LoggerFactory.getLogger(AbstractTransactCommand.class);
49 protected static final UUID TXUUID = new UUID("TXUUID");
50 protected volatile HwvtepOperationalState hwvtepOperationalState = null;
51 protected volatile TransactionBuilder deviceTransaction = null;
52 private Collection<DataTreeModification<Node>> changes;
53 Set<MdsalUpdate<T>> updates = new HashSet<>();
55 protected AbstractTransactCommand() {
59 public AbstractTransactCommand(final HwvtepOperationalState state,
60 final Collection<DataTreeModification<Node>> changes) {
61 this.hwvtepOperationalState = state;
62 this.changes = changes;
65 public HwvtepOperationalState getOperationalState() {
66 return hwvtepOperationalState;
69 public DataBroker getDataBroker() {
70 return getOperationalState().getDataBroker();
73 public Collection<DataTreeModification<Node>> getChanges() {
77 void updateCurrentTxDeleteData(final Class<? extends Identifiable> cls, final InstanceIdentifier key,
79 hwvtepOperationalState.updateCurrentTxDeleteData(cls, key);
80 markKeyAsInTransit(cls, key);
81 addToUpdates(key, data);
84 void updateCurrentTxData(final Class<? extends Identifiable> cls, final InstanceIdentifier key, final UUID uuid,
86 hwvtepOperationalState.updateCurrentTxData(cls, key, uuid);
87 markKeyAsInTransit(cls, key);
88 addToUpdates(key, data);
91 void addToUpdates(final InstanceIdentifier key, final T data) {
93 Type type = getClass().getGenericSuperclass();
94 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
95 if (getConfigData((Class<? extends Identifiable>) classType, key) != null) {
96 oldData = (T) getConfigData((Class<? extends Identifiable>) classType, key).getData();
98 updates.add(new MdsalUpdate<>(key, data, oldData));
101 void processDependencies(final UnMetDependencyGetter<T> unMetDependencyGetter,
102 final TransactionBuilder transaction,
103 final InstanceIdentifier<Node> nodeIid,
104 final InstanceIdentifier key,
105 final T data, final Object... extraData) {
107 HwvtepDeviceInfo deviceInfo = hwvtepOperationalState.getDeviceInfo();
108 Type type = getClass().getGenericSuperclass();
109 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
110 Map inTransitDependencies = Collections.emptyMap();
111 Map confingDependencies = Collections.emptyMap();
114 if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
115 inTransitDependencies = new HashMap<>();
116 inTransitDependencies.put(classType, Lists.newArrayList(key));
119 inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(hwvtepOperationalState, data);
120 confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(hwvtepOperationalState, data);
121 //we can skip the config termination point dependency as we can create them in device as part of this tx
122 confingDependencies.remove(TerminationPoint.class);
124 //If this key itself is in transit wait for the response of this key itself
125 if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)
126 || deviceInfo.isKeyInDependencyQueue(key)) {
127 inTransitDependencies.put(classType, Lists.newArrayList(key));
130 LOG.info("Update received for key: {} txId: {}", key, getOperationalState().getTransactionId());
131 if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies)
132 && HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
133 doDeviceTransaction(transaction, nodeIid, data, key, extraData);
135 getDeviceInfo().clearConfigData((Class<? extends Identifiable>) classType, key);
137 getDeviceInfo().updateConfigData((Class<? extends Identifiable>) classType, key, data);
141 if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
142 DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
143 key, data, confingDependencies) {
144 AbstractTransactCommand clone = getClone();
147 public void onDependencyResolved(final HwvtepOperationalState operationalState,
148 final TransactionBuilder transactionBuilder) {
149 clone.hwvtepOperationalState = operationalState;
150 HwvtepDeviceInfo.DeviceData deviceData =
151 getDeviceInfo().getConfigData((Class<? extends Identifiable>)getClassType(), key);
153 if (deviceData != null && deviceData.getData() != null) {
154 latest = (T) deviceData.getData();
155 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
156 } else if (isDeleteCmd()) {
157 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
162 public void onFailure() {
163 clone.onFailure(transaction);
167 public void onSuccess() {
168 clone.onSuccess(transaction);
171 LOG.info("Update Adding to config wait queue for key: {} txId: {}",
172 key, getOperationalState().getTransactionId());
173 addJobToQueue(configWaitingJob);
176 final long transactionId = hwvtepOperationalState.getTransactionId();
177 if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
179 DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
180 key, data, inTransitDependencies, transactionId) {
181 AbstractTransactCommand clone = getClone();
184 public void onDependencyResolved(final HwvtepOperationalState operationalState,
185 final TransactionBuilder transactionBuilder) {
186 clone.hwvtepOperationalState = operationalState;
187 HwvtepDeviceInfo.DeviceData deviceData = getDeviceInfo()
188 .getConfigData((Class<? extends Identifiable>)getClassType(), key);
190 if (deviceData != null && deviceData.getData() != null) {
191 latest = (T) deviceData.getData();
192 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
193 } else if (isDeleteCmd()) {
194 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
199 public void onFailure() {
200 clone.onFailure(transaction);
204 public void onSuccess() {
205 clone.onSuccess(transaction);
208 LOG.info("Update Adding to op wait queue for key: {} txId: {}", key, transactionId);
209 addJobToQueue(opWaitingJob);
215 public void doDeviceTransaction(final TransactionBuilder transaction, final InstanceIdentifier<Node> nodeIid,
216 final T data, final InstanceIdentifier key, final Object... extraData) {
217 //tobe removed as part of refactoring patch
221 public void onConfigUpdate(final TransactionBuilder transaction, final InstanceIdentifier<Node> nodeIid,
222 final T data, final InstanceIdentifier key, final Object... extraData) {
223 //tobe removed as part of refactoring patch
226 protected A augmentation(final Node node) {
230 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
231 Class<? extends Augmentation<Node>> augType =
232 (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
233 Augmentation<Node> augmentation = node.augmentation(augType);
234 return (A) augmentation;
237 protected Map<I, T> getData(final A augmentation) {
238 return Collections.emptyMap();
241 protected List<T> getData(final Node node) {
242 A augmentation = augmentation(node);
243 if (augmentation != null) {
244 Map<I, T> data = getData(augmentation);
246 // TODO: why are we performing a copy here?
247 return new ArrayList<>(data.values());
250 return Collections.emptyList();
254 protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
255 final Collection<DataTreeModification<Node>> modification, final Class<T> class1) {
256 Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
257 if (modification != null && !modification.isEmpty()) {
258 for (DataTreeModification<Node> change : modification) {
259 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
260 if (!Objects.equals(hwvtepOperationalState.getConnectionInstance().getInstanceIdentifier(), key)) {
263 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
265 if (getOperationalState().isInReconciliation()) {
266 removed = getRemoved(change);
268 removed = (List<T>) getOperationalState().getDeletedData(key, classType);
270 removed.addAll(getCascadeDeleteData(change));
271 result.put(key, removed);
278 protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
279 final Collection<DataTreeModification<Node>> modification, final Class<T> class1) {
280 Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
281 if (modification != null && !modification.isEmpty()) {
282 for (DataTreeModification<Node> change : modification) {
283 InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
284 if (!Objects.equals(hwvtepOperationalState.getConnectionInstance().getInstanceIdentifier(), key)) {
287 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
288 List<T> updated = null;
289 if (getOperationalState().isInReconciliation()) {
290 updated = getUpdated(change);
292 updated = (List<T>) getOperationalState().getUpdatedData(key, classType);
294 result.put(key, updated);
300 List<T> getCascadeDeleteData(final DataTreeModification<Node> change) {
301 if (!cascadeDelete()) {
302 return Collections.emptyList();
304 DataObjectModification<Node> mod = change.getRootNode();
305 Node updatedNode = TransactUtils.getUpdated(mod);
306 List<T> updatedData = getData(updatedNode);
307 Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
308 UnMetDependencyGetter dependencyGetter = getDependencyGetter();
309 if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData)
310 && dependencyGetter != null) {
311 List<T> removed = new ArrayList<>();
312 for (T ele : updatedData) {
313 if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
319 return Collections.emptyList();
322 List<T> getRemoved(final DataTreeModification<Node> change) {
323 DataObjectModification<Node> mod = change.getRootNode();
325 Node removed = TransactUtils.getRemoved(mod);
326 Node updated = TransactUtils.getUpdated(mod);
327 Node before = mod.getDataBefore();
328 return diffOf(removed, before, updated, true);
331 List<T> getUpdated(final DataTreeModification<Node> change) {
332 DataObjectModification<Node> mod = change.getRootNode();
333 Node updated = TransactUtils.getUpdated(mod);
334 Node before = mod.getDataBefore();
335 return diffOf(updated, before, false);
338 List<T> diffOf(final Node include, final Node node1, final Node node2, final boolean compareKeyOnly) {
339 List<T> data1 = getData(include);
340 List<T> data2 = diffOf(node1, node2, compareKeyOnly);
341 if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
342 return Collections.EMPTY_LIST;
344 List<T> result = new ArrayList<>(data1);
345 result.addAll(data2);
349 List<T> diffOf(final Node node1, final Node node2, final boolean compareKeyOnly) {
350 List<T> result = new ArrayList<>();
352 List<T> list1 = getData(node1);
353 List<T> list2 = getData(node2);
355 if (HwvtepSouthboundUtil.isEmpty(list1)) {
356 return Collections.EMPTY_LIST;
358 if (HwvtepSouthboundUtil.isEmpty(list2)) {
359 return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.EMPTY_LIST : list1;
362 Map<Object, T> map1 = list1.stream().collect(Collectors.toMap(ele -> ele.key(), ele -> ele));
363 Map<Object, T> map2 = list2.stream().collect(Collectors.toMap(ele -> ele.key(), ele -> ele));
364 map1.entrySet().forEach(entry1 -> {
365 T val2 = map2.remove(entry1.getKey());
366 if (compareKeyOnly) {
368 result.add(entry1.getValue());
372 result.add(entry1.getValue());
375 if (!areEqual(entry1.getValue(), val2)) {
376 result.add(entry1.getValue());
384 protected Type getClassType() {
385 Type type = getClass().getGenericSuperclass();
386 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
390 protected boolean areEqual(final T obj1, final T obj2) {
391 return obj1.key().equals(obj2.key());
394 protected UnMetDependencyGetter getDependencyGetter() {
399 * Tells if this object needs to be deleted if its dependent object gets deleted
400 * Ex : LocalUcastMac and LocalMacstMac.
402 * @return true if this object needs to be deleted if its dependent object gets deleted
404 protected boolean cascadeDelete() {
408 protected boolean isDeleteCmd() {
412 protected HwvtepDeviceInfo getDeviceInfo() {
413 return getOperationalState().getDeviceInfo();
416 protected TransactionBuilder getDeviceTransaction() {
417 return deviceTransaction;
421 public void onSuccess(final TransactionBuilder deviceTx) {
422 onCommandSucceeded();
426 public void onFailure(final TransactionBuilder deviceTx) {
430 protected void onCommandSucceeded() {
433 protected void onCommandFailed() {
436 void updateControllerTxHistory(final TransactionType transactionType, final Object element) {
437 getOperationalState().getDeviceInfo().addToControllerTx(transactionType, element);
440 public <T> HwvtepDeviceInfo.DeviceData fetchDeviceData(final Class<? extends Identifiable> cls,
441 final InstanceIdentifier key) {
442 HwvtepDeviceInfo.DeviceData deviceData = getDeviceOpData(cls, key);
443 if (deviceData == null) {
444 LOG.debug("Could not find data for key {}", key);
445 java.util.Optional<TypedBaseTable> optional =
446 getTableReader().getHwvtepTableEntryUUID(cls, key, null);
447 if (optional.isPresent()) {
448 LOG.debug("Found the data for key from device {} ", key);
449 getDeviceInfo().updateDeviceOperData(cls, key, optional.get().getUuid(), optional.get());
450 return getDeviceOpData(cls, key);
452 LOG.info("Could not Find the data for key from device {} ", key);
458 protected String getKeyStr(final InstanceIdentifier iid) {
459 return iid.toString();
462 public <K extends Identifiable> void addJobToQueue(final DependentJob<K> job) {
463 hwvtepOperationalState.getDeviceInfo().putKeyInDependencyQueue(job.getKey());
464 hwvtepOperationalState.getDeviceInfo().addJobToQueue(job);
467 public void markKeyAsInTransit(final Class<? extends Identifiable> cls, final InstanceIdentifier key) {
468 hwvtepOperationalState.getDeviceInfo().markKeyAsInTransit(cls, key);
471 public HwvtepDeviceInfo.DeviceData getDeviceOpData(final Class<? extends Identifiable> cls,
472 final InstanceIdentifier key) {
473 return getOperationalState().getDeviceInfo().getDeviceOperData(cls, key);
476 public void clearConfigData(final Class<? extends Identifiable> cls, final InstanceIdentifier key) {
477 hwvtepOperationalState.getDeviceInfo().clearConfigData(cls, key);
480 public HwvtepDeviceInfo.DeviceData getConfigData(final Class<? extends Identifiable> cls,
481 final InstanceIdentifier key) {
482 return hwvtepOperationalState.getDeviceInfo().getConfigData(cls, key);
485 public void updateConfigData(final Class<? extends Identifiable> cls, final InstanceIdentifier key,
487 hwvtepOperationalState.getDeviceInfo().updateConfigData(cls, key, data);
490 @SuppressWarnings("checkstyle:IllegalCatch")
491 public AbstractTransactCommand getClone() {
493 return getClass().getConstructor(HwvtepOperationalState.class, Collection.class)
494 .newInstance(hwvtepOperationalState, changes);
495 } catch (Throwable e) {
496 LOG.error("Failed to clone the cmd ", e);
501 public HwvtepTableReader getTableReader() {
502 return getOperationalState().getConnectionInstance().getHwvtepTableReader();
505 public HwvtepConnectionInstance getConnectionInstance() {
506 return hwvtepOperationalState.getConnectionInstance();
509 public HwvtepOperationalState newOperState() {
510 return new HwvtepOperationalState(getConnectionInstance());