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.DataObject;
40 import org.opendaylight.yangtools.yang.binding.Identifiable;
41 import org.opendaylight.yangtools.yang.binding.Identifier;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 public abstract class AbstractTransactCommand<T extends Identifiable<I> & DataObject, I extends Identifier<T>,
47 A extends Augmentation<Node>> implements TransactCommand<T> {
49 private static final Logger LOG = LoggerFactory.getLogger(AbstractTransactCommand.class);
50 protected static final UUID TXUUID = new UUID("TXUUID");
51 protected volatile HwvtepOperationalState hwvtepOperationalState = null;
52 protected volatile TransactionBuilder deviceTransaction = null;
53 private Collection<DataTreeModification<Node>> changes;
54 Set<MdsalUpdate<T>> updates = new HashSet<>();
56 protected AbstractTransactCommand() {
60 public AbstractTransactCommand(final HwvtepOperationalState state,
61 final 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(final Class<? extends Identifiable> cls, final InstanceIdentifier key,
80 hwvtepOperationalState.updateCurrentTxDeleteData(cls, key);
81 markKeyAsInTransit(cls, key);
82 addToUpdates(key, data);
85 void updateCurrentTxData(final Class<? extends Identifiable> cls, final InstanceIdentifier key, final UUID uuid,
87 hwvtepOperationalState.updateCurrentTxData(cls, key, uuid);
88 markKeyAsInTransit(cls, key);
89 addToUpdates(key, data);
92 void addToUpdates(final InstanceIdentifier key, final T data) {
94 Type type = getClass().getGenericSuperclass();
95 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
96 if (getConfigData((Class<? extends Identifiable>) classType, key) != null) {
97 oldData = (T) getConfigData((Class<? extends Identifiable>) classType, key).getData();
99 updates.add(new MdsalUpdate<>(key, data, oldData));
102 void processDependencies(final UnMetDependencyGetter<T> unMetDependencyGetter,
103 final TransactionBuilder transaction,
104 final InstanceIdentifier<Node> nodeIid,
105 final InstanceIdentifier key,
106 final T data, final Object... extraData) {
108 HwvtepDeviceInfo deviceInfo = hwvtepOperationalState.getDeviceInfo();
109 Type type = getClass().getGenericSuperclass();
110 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
111 Map inTransitDependencies = Collections.emptyMap();
112 Map confingDependencies = Collections.emptyMap();
115 if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
116 inTransitDependencies = new HashMap<>();
117 inTransitDependencies.put(classType, Lists.newArrayList(key));
120 inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(hwvtepOperationalState, data);
121 confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(hwvtepOperationalState, data);
122 //we can skip the config termination point dependency as we can create them in device as part of this tx
123 confingDependencies.remove(TerminationPoint.class);
125 //If this key itself is in transit wait for the response of this key itself
126 if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)
127 || deviceInfo.isKeyInDependencyQueue(key)) {
128 inTransitDependencies.put(classType, Lists.newArrayList(key));
131 LOG.info("Update received for key: {} txId: {}", getNodeKeyStr(key), getOperationalState().getTransactionId());
132 if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies)
133 && HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
134 doDeviceTransaction(transaction, nodeIid, data, key, extraData);
136 getDeviceInfo().clearConfigData((Class<? extends Identifiable>) classType, key);
138 getDeviceInfo().updateConfigData((Class<? extends Identifiable>) classType, key, data);
142 if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
143 DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
144 key, data, confingDependencies) {
145 AbstractTransactCommand clone = getClone();
148 public void onDependencyResolved(final HwvtepOperationalState operationalState,
149 final TransactionBuilder transactionBuilder) {
150 clone.hwvtepOperationalState = operationalState;
151 HwvtepDeviceInfo.DeviceData deviceData =
152 getDeviceInfo().getConfigData((Class<? extends Identifiable>)getClassType(), key);
154 if (deviceData != null && deviceData.getData() != null) {
155 latest = (T) deviceData.getData();
156 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
157 } else if (isDeleteCmd()) {
158 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
163 public void onFailure() {
164 clone.onFailure(transaction);
168 public void onSuccess() {
169 clone.onSuccess(transaction);
172 LOG.info("Update Adding to config wait queue for key: {} txId: {}",
173 key, getOperationalState().getTransactionId());
174 addJobToQueue(configWaitingJob);
177 final long transactionId = hwvtepOperationalState.getTransactionId();
178 if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
180 DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
181 key, data, inTransitDependencies, transactionId) {
182 AbstractTransactCommand clone = getClone();
185 public void onDependencyResolved(final HwvtepOperationalState operationalState,
186 final TransactionBuilder transactionBuilder) {
187 clone.hwvtepOperationalState = operationalState;
188 HwvtepDeviceInfo.DeviceData deviceData = getDeviceInfo()
189 .getConfigData((Class<? extends Identifiable>)getClassType(), key);
191 if (deviceData != null && deviceData.getData() != null) {
192 latest = (T) deviceData.getData();
193 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
194 } else if (isDeleteCmd()) {
195 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
200 public void onFailure() {
201 clone.onFailure(transaction);
205 public void onSuccess() {
206 clone.onSuccess(transaction);
209 LOG.info("Update Adding to op wait queue for key: {} txId: {}", getNodeKeyStr(key), transactionId);
210 addJobToQueue(opWaitingJob);
216 public void doDeviceTransaction(final TransactionBuilder transaction, final InstanceIdentifier<Node> nodeIid,
217 final T data, final InstanceIdentifier key, final Object... extraData) {
218 //tobe removed as part of refactoring patch
222 public void onConfigUpdate(final TransactionBuilder transaction, final InstanceIdentifier<Node> nodeIid,
223 final T data, final InstanceIdentifier key, final Object... extraData) {
224 //tobe removed as part of refactoring patch
227 protected A augmentation(final Node node) {
231 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
232 Class<? extends Augmentation<Node>> augType =
233 (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
234 Augmentation<Node> augmentation = node.augmentation(augType);
235 return (A) augmentation;
238 protected Map<I, T> getData(final A augmentation) {
239 return Collections.emptyMap();
242 protected List<T> getData(final Node node) {
243 A augmentation = augmentation(node);
244 if (augmentation != null) {
245 Map<I, T> data = getData(augmentation);
247 // TODO: why are we performing a copy here?
248 return new ArrayList<>(data.values());
251 return Collections.emptyList();
255 protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
256 final Collection<DataTreeModification<Node>> modification, final Class<T> class1) {
257 Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
258 if (modification != null && !modification.isEmpty()) {
259 for (DataTreeModification<Node> change : modification) {
260 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
261 if (!Objects.equals(hwvtepOperationalState.getConnectionInstance().getInstanceIdentifier(), key)) {
264 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
266 if (getOperationalState().isInReconciliation()) {
267 removed = getRemoved(change);
269 removed = (List<T>) getOperationalState().getDeletedData(key, classType);
271 removed.addAll(getCascadeDeleteData(change));
272 result.put(key, removed);
279 protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
280 final Collection<DataTreeModification<Node>> modification, final Class<T> class1) {
281 Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
282 if (modification != null && !modification.isEmpty()) {
283 for (DataTreeModification<Node> change : modification) {
284 InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
285 if (!Objects.equals(hwvtepOperationalState.getConnectionInstance().getInstanceIdentifier(), key)) {
288 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
289 List<T> updated = null;
290 if (getOperationalState().isInReconciliation()) {
291 updated = getUpdated(change);
293 updated = (List<T>) getOperationalState().getUpdatedData(key, classType);
295 result.put(key, updated);
301 List<T> getCascadeDeleteData(final DataTreeModification<Node> change) {
302 if (!cascadeDelete()) {
303 return Collections.emptyList();
305 DataObjectModification<Node> mod = change.getRootNode();
306 Node updatedNode = TransactUtils.getUpdated(mod);
307 List<T> updatedData = getData(updatedNode);
308 Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
309 UnMetDependencyGetter dependencyGetter = getDependencyGetter();
310 if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData)
311 && dependencyGetter != null) {
312 List<T> removed = new ArrayList<>();
313 for (T ele : updatedData) {
314 if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
320 return Collections.emptyList();
323 List<T> getRemoved(final DataTreeModification<Node> change) {
324 DataObjectModification<Node> mod = change.getRootNode();
326 Node removed = TransactUtils.getRemoved(mod);
327 Node updated = TransactUtils.getUpdated(mod);
328 Node before = mod.getDataBefore();
329 return diffOf(removed, before, updated, true);
332 List<T> getUpdated(final DataTreeModification<Node> change) {
333 DataObjectModification<Node> mod = change.getRootNode();
334 Node updated = TransactUtils.getUpdated(mod);
335 Node before = mod.getDataBefore();
336 return diffOf(updated, before, false);
339 List<T> diffOf(final Node include, final Node node1, final Node node2, final boolean compareKeyOnly) {
340 List<T> data1 = getData(include);
341 List<T> data2 = diffOf(node1, node2, compareKeyOnly);
342 if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
343 return Collections.emptyList();
345 List<T> result = new ArrayList<>(data1);
346 result.addAll(data2);
350 List<T> diffOf(final Node node1, final Node node2, final boolean compareKeyOnly) {
351 List<T> result = new ArrayList<>();
353 List<T> list1 = getData(node1);
354 List<T> list2 = getData(node2);
356 if (HwvtepSouthboundUtil.isEmpty(list1)) {
357 return Collections.emptyList();
359 if (HwvtepSouthboundUtil.isEmpty(list2)) {
360 return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.emptyList() : list1;
363 Map<Object, T> map1 = list1.stream().collect(Collectors.toMap(Identifiable::key, ele -> ele));
364 Map<Object, T> map2 = list2.stream().collect(Collectors.toMap(Identifiable::key, ele -> ele));
365 map1.entrySet().forEach(entry1 -> {
366 T val2 = map2.remove(entry1.getKey());
367 if (compareKeyOnly) {
369 result.add(entry1.getValue());
373 result.add(entry1.getValue());
376 if (!areEqual(entry1.getValue(), val2)) {
377 result.add(entry1.getValue());
385 protected Type getClassType() {
386 Type type = getClass().getGenericSuperclass();
387 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
391 protected boolean areEqual(final T obj1, final T obj2) {
392 return obj1.key().equals(obj2.key());
395 protected UnMetDependencyGetter<T> getDependencyGetter() {
400 * Tells if this object needs to be deleted if its dependent object gets deleted
401 * Ex : LocalUcastMac and LocalMacstMac.
403 * @return true if this object needs to be deleted if its dependent object gets deleted
405 protected boolean cascadeDelete() {
409 protected boolean isDeleteCmd() {
413 protected HwvtepDeviceInfo getDeviceInfo() {
414 return getOperationalState().getDeviceInfo();
417 protected TransactionBuilder getDeviceTransaction() {
418 return deviceTransaction;
422 public void onSuccess(final TransactionBuilder deviceTx) {
423 onCommandSucceeded();
427 public void onFailure(final TransactionBuilder deviceTx) {
431 protected void onCommandSucceeded() {
434 protected void onCommandFailed() {
437 void updateControllerTxHistory(final TransactionType transactionType, final Object element) {
438 getOperationalState().getDeviceInfo().addToControllerTx(transactionType, element);
441 public <T> HwvtepDeviceInfo.DeviceData fetchDeviceData(final Class<? extends Identifiable> cls,
442 final InstanceIdentifier key) {
443 HwvtepDeviceInfo.DeviceData deviceData = getDeviceOpData(cls, key);
444 if (deviceData == null) {
445 LOG.debug("Could not find data for key {}", getNodeKeyStr(key));
446 java.util.Optional<TypedBaseTable> optional =
447 getTableReader().getHwvtepTableEntryUUID(cls, key, null);
448 if (optional.isPresent()) {
449 LOG.debug("Found the data for key from device {} ", getNodeKeyStr(key));
450 getDeviceInfo().updateDeviceOperData(cls, key, optional.get().getUuid(), optional.get());
451 return getDeviceOpData(cls, key);
453 LOG.info("Could not Find the data for key from device {} ", getNodeKeyStr(key));
459 public <K extends Identifiable> void addJobToQueue(final DependentJob<K> job) {
460 hwvtepOperationalState.getDeviceInfo().putKeyInDependencyQueue(job.getKey());
461 hwvtepOperationalState.getDeviceInfo().addJobToQueue(job);
464 public void markKeyAsInTransit(final Class<? extends Identifiable> cls, final InstanceIdentifier key) {
465 hwvtepOperationalState.getDeviceInfo().markKeyAsInTransit(cls, key);
468 public HwvtepDeviceInfo.DeviceData getDeviceOpData(final Class<? extends Identifiable> cls,
469 final InstanceIdentifier key) {
470 return getOperationalState().getDeviceInfo().getDeviceOperData(cls, key);
473 public void clearConfigData(final Class<? extends Identifiable> cls, final InstanceIdentifier key) {
474 hwvtepOperationalState.getDeviceInfo().clearConfigData(cls, key);
477 public HwvtepDeviceInfo.DeviceData getConfigData(final Class<? extends Identifiable> cls,
478 final InstanceIdentifier key) {
479 return hwvtepOperationalState.getDeviceInfo().getConfigData(cls, key);
482 public void updateConfigData(final Class<? extends Identifiable> cls, final InstanceIdentifier key,
484 hwvtepOperationalState.getDeviceInfo().updateConfigData(cls, key, data);
487 @SuppressWarnings("checkstyle:IllegalCatch")
488 public AbstractTransactCommand getClone() {
490 return getClass().getConstructor(HwvtepOperationalState.class, Collection.class)
491 .newInstance(hwvtepOperationalState, changes);
492 } catch (Throwable e) {
493 LOG.error("Failed to clone the cmd ", e);
498 public HwvtepTableReader getTableReader() {
499 return getOperationalState().getConnectionInstance().getHwvtepTableReader();
502 public HwvtepConnectionInstance getConnectionInstance() {
503 return hwvtepOperationalState.getConnectionInstance();
506 public HwvtepOperationalState newOperState() {
507 return new HwvtepOperationalState(getConnectionInstance());
510 protected String getNodeKeyStr(final InstanceIdentifier<T> iid) {
511 return getClassType().getTypeName() + "."
512 + iid.firstKeyOf(Node.class).getNodeId().getValue() + "." + getKeyStr(iid);
515 protected String getKeyStr(final InstanceIdentifier<T> iid) {
516 return iid.toString();
519 protected String getLsKeyStr(final InstanceIdentifier iid) {
520 return ((InstanceIdentifier<LogicalSwitches>)iid).firstKeyOf(LogicalSwitches.class)
521 .getHwvtepNodeName().getValue();