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;
21 import java.util.Optional;
23 import java.util.stream.Collectors;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.opendaylight.mdsal.binding.api.DataBroker;
26 import org.opendaylight.mdsal.binding.api.DataObjectModification;
27 import org.opendaylight.mdsal.binding.api.DataTreeModification;
28 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepConnectionInstance;
29 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
30 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil;
31 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepTableReader;
32 import org.opendaylight.ovsdb.lib.notation.UUID;
33 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
34 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
35 import org.opendaylight.ovsdb.utils.mdsal.utils.TransactionType;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
39 import org.opendaylight.yangtools.yang.binding.Augmentation;
40 import org.opendaylight.yangtools.yang.binding.DataObject;
41 import org.opendaylight.yangtools.yang.binding.Identifiable;
42 import org.opendaylight.yangtools.yang.binding.Identifier;
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<I> & DataObject, I extends Identifier<T>,
48 A extends Augmentation<Node>> 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(final HwvtepOperationalState state,
62 final Collection<DataTreeModification<Node>> changes) {
63 this.hwvtepOperationalState = state;
64 this.changes = changes;
67 public HwvtepOperationalState getOperationalState() {
68 return hwvtepOperationalState;
71 public DataBroker getDataBroker() {
72 return getOperationalState().getDataBroker();
75 public Collection<DataTreeModification<Node>> getChanges() {
79 void updateCurrentTxDeleteData(final Class<? extends Identifiable> cls, final InstanceIdentifier key,
81 hwvtepOperationalState.updateCurrentTxDeleteData(cls, key);
82 markKeyAsInTransit(cls, key);
83 addToUpdates(key, data);
86 void updateCurrentTxData(final Class<? extends Identifiable> cls, final InstanceIdentifier key, final UUID uuid,
88 hwvtepOperationalState.updateCurrentTxData(cls, key, uuid);
89 markKeyAsInTransit(cls, key);
90 addToUpdates(key, data);
93 void addToUpdates(final InstanceIdentifier key, final T data) {
95 Type type = getClass().getGenericSuperclass();
96 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
97 if (getConfigData((Class<? extends Identifiable>) classType, key) != null) {
98 oldData = (T) getConfigData((Class<? extends Identifiable>) classType, key).getData();
100 updates.add(new MdsalUpdate<>(key, data, oldData));
103 void processDependencies(final UnMetDependencyGetter<T> unMetDependencyGetter,
104 final TransactionBuilder transaction,
105 final InstanceIdentifier<Node> nodeIid,
106 final InstanceIdentifier key,
107 final T data, final Object... extraData) {
109 HwvtepDeviceInfo deviceInfo = hwvtepOperationalState.getDeviceInfo();
110 Type type = getClass().getGenericSuperclass();
111 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
112 Map inTransitDependencies = Collections.emptyMap();
113 Map confingDependencies = Collections.emptyMap();
116 if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
117 inTransitDependencies = new HashMap<>();
118 inTransitDependencies.put(classType, Lists.newArrayList(key));
121 inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(hwvtepOperationalState, data);
122 confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(hwvtepOperationalState, data);
123 //we can skip the config termination point dependency as we can create them in device as part of this tx
124 confingDependencies.remove(TerminationPoint.class);
126 //If this key itself is in transit wait for the response of this key itself
127 if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)
128 || deviceInfo.isKeyInDependencyQueue(key)) {
129 inTransitDependencies.put(classType, Lists.newArrayList(key));
132 LOG.info("Update received for key: {} txId: {}", getNodeKeyStr(key), getOperationalState().getTransactionId());
133 if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies)
134 && HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
135 doDeviceTransaction(transaction, nodeIid, data, key, extraData);
137 getDeviceInfo().clearConfigData((Class<? extends Identifiable>) classType, key);
139 getDeviceInfo().updateConfigData((Class<? extends Identifiable>) classType, key, data);
143 if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
144 DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
145 key, data, confingDependencies) {
146 AbstractTransactCommand clone = getClone();
149 public void onDependencyResolved(final HwvtepOperationalState operationalState,
150 final TransactionBuilder transactionBuilder) {
151 clone.hwvtepOperationalState = operationalState;
152 HwvtepDeviceInfo.DeviceData deviceData =
153 getDeviceInfo().getConfigData((Class<? extends Identifiable>)getClassType(), key);
155 if (deviceData != null && deviceData.getData() != null) {
156 latest = (T) deviceData.getData();
157 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
158 } else if (isDeleteCmd()) {
159 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
164 public void onFailure() {
165 clone.onFailure(transaction);
169 public void onSuccess() {
170 clone.onSuccess(transaction);
173 LOG.info("Update Adding to config wait queue for key: {} txId: {}",
174 key, getOperationalState().getTransactionId());
175 addJobToQueue(configWaitingJob);
178 final long transactionId = hwvtepOperationalState.getTransactionId();
179 if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
181 DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
182 key, data, inTransitDependencies, transactionId) {
183 AbstractTransactCommand clone = getClone();
186 public void onDependencyResolved(final HwvtepOperationalState operationalState,
187 final TransactionBuilder transactionBuilder) {
188 clone.hwvtepOperationalState = operationalState;
189 HwvtepDeviceInfo.DeviceData deviceData = getDeviceInfo()
190 .getConfigData((Class<? extends Identifiable>)getClassType(), key);
192 if (deviceData != null && deviceData.getData() != null) {
193 latest = (T) deviceData.getData();
194 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
195 } else if (isDeleteCmd()) {
196 clone.onConfigUpdate(transactionBuilder, nodeIid, latest, key, extraData);
201 public void onFailure() {
202 clone.onFailure(transaction);
206 public void onSuccess() {
207 clone.onSuccess(transaction);
210 LOG.info("Update Adding to op wait queue for key: {} txId: {}", getNodeKeyStr(key), transactionId);
211 addJobToQueue(opWaitingJob);
217 public void doDeviceTransaction(final TransactionBuilder transaction, final InstanceIdentifier<Node> nodeIid,
218 final T data, final InstanceIdentifier key, final Object... extraData) {
219 //tobe removed as part of refactoring patch
223 public void onConfigUpdate(final TransactionBuilder transaction, final InstanceIdentifier<Node> nodeIid,
224 final T data, final InstanceIdentifier key, final Object... extraData) {
225 //tobe removed as part of refactoring patch
228 protected A augmentation(final Node node) {
232 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
233 Class<? extends Augmentation<Node>> augType =
234 (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
235 Augmentation<Node> augmentation = node.augmentation(augType);
236 return (A) augmentation;
239 protected Map<I, T> getData(final A augmentation) {
240 return Collections.emptyMap();
243 protected List<T> getData(final Node node) {
244 A augmentation = augmentation(node);
245 if (augmentation != null) {
246 Map<I, T> data = getData(augmentation);
248 // TODO: why are we performing a copy here?
249 return new ArrayList<>(data.values());
252 return Collections.emptyList();
256 protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
257 final Collection<DataTreeModification<Node>> modification, final Class<T> class1) {
258 Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
259 if (modification != null && !modification.isEmpty()) {
260 for (DataTreeModification<Node> change : modification) {
261 final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
262 if (!Objects.equals(hwvtepOperationalState.getConnectionInstance().getInstanceIdentifier(), key)) {
265 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
267 if (getOperationalState().isInReconciliation()) {
268 removed = getRemoved(change);
270 removed = (List<T>) getOperationalState().getDeletedData(key, classType);
272 removed.addAll(getCascadeDeleteData(change));
273 result.put(key, removed);
280 protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
281 final Collection<DataTreeModification<Node>> modification, final Class<T> class1) {
282 Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
283 if (modification != null && !modification.isEmpty()) {
284 for (DataTreeModification<Node> change : modification) {
285 InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
286 if (!Objects.equals(hwvtepOperationalState.getConnectionInstance().getInstanceIdentifier(), key)) {
289 Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
290 List<T> updated = null;
291 if (getOperationalState().isInReconciliation()) {
292 updated = getUpdated(change);
294 updated = (List<T>) getOperationalState().getUpdatedData(key, classType);
296 result.put(key, updated);
302 List<T> getCascadeDeleteData(final DataTreeModification<Node> change) {
303 if (!cascadeDelete()) {
304 return Collections.emptyList();
306 DataObjectModification<Node> mod = change.getRootNode();
307 Node updatedNode = TransactUtils.getUpdated(mod);
308 List<T> updatedData = getData(updatedNode);
309 Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
310 UnMetDependencyGetter dependencyGetter = getDependencyGetter();
311 if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData)
312 && dependencyGetter != null) {
313 List<T> removed = new ArrayList<>();
314 for (T ele : updatedData) {
315 if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
321 return Collections.emptyList();
324 List<T> getRemoved(final DataTreeModification<Node> change) {
325 DataObjectModification<Node> mod = change.getRootNode();
327 Node removed = TransactUtils.getRemoved(mod);
328 Node updated = TransactUtils.getUpdated(mod);
329 Node before = mod.getDataBefore();
330 return diffOf(removed, before, updated, true);
333 List<T> getUpdated(final DataTreeModification<Node> change) {
334 DataObjectModification<Node> mod = change.getRootNode();
335 Node updated = TransactUtils.getUpdated(mod);
336 Node before = mod.getDataBefore();
337 return diffOf(updated, before, false);
340 List<T> diffOf(final Node include, final Node node1, final Node node2, final boolean compareKeyOnly) {
341 List<T> data1 = getData(include);
342 List<T> data2 = diffOf(node1, node2, compareKeyOnly);
343 if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
344 return Collections.emptyList();
346 List<T> result = new ArrayList<>(data1);
347 result.addAll(data2);
351 List<T> diffOf(final Node node1, final Node node2, final boolean compareKeyOnly) {
352 List<T> result = new ArrayList<>();
354 List<T> list1 = getData(node1);
355 List<T> list2 = getData(node2);
357 if (HwvtepSouthboundUtil.isEmpty(list1)) {
358 return Collections.emptyList();
360 if (HwvtepSouthboundUtil.isEmpty(list2)) {
361 return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.emptyList() : list1;
364 Map<Object, T> map1 = list1.stream().collect(Collectors.toMap(Identifiable::key, ele -> ele));
365 Map<Object, T> map2 = list2.stream().collect(Collectors.toMap(Identifiable::key, ele -> ele));
366 map1.entrySet().forEach(entry1 -> {
367 T val2 = map2.remove(entry1.getKey());
368 if (compareKeyOnly) {
370 result.add(entry1.getValue());
374 result.add(entry1.getValue());
377 if (!areEqual(entry1.getValue(), val2)) {
378 result.add(entry1.getValue());
386 protected Type getClassType() {
387 Type type = getClass().getGenericSuperclass();
388 Type classType = ((ParameterizedType) type).getActualTypeArguments()[0];
392 protected boolean areEqual(final T obj1, final T obj2) {
393 return obj1.key().equals(obj2.key());
396 protected UnMetDependencyGetter<T> getDependencyGetter() {
401 * Tells if this object needs to be deleted if its dependent object gets deleted
402 * Ex : LocalUcastMac and LocalMacstMac.
404 * @return true if this object needs to be deleted if its dependent object gets deleted
406 protected boolean cascadeDelete() {
410 protected boolean isDeleteCmd() {
414 protected HwvtepDeviceInfo getDeviceInfo() {
415 return getOperationalState().getDeviceInfo();
418 protected TransactionBuilder getDeviceTransaction() {
419 return deviceTransaction;
423 public void onSuccess(final TransactionBuilder deviceTx) {
424 onCommandSucceeded();
428 public void onFailure(final TransactionBuilder deviceTx) {
432 protected void onCommandSucceeded() {
435 protected void onCommandFailed() {
438 void updateControllerTxHistory(final TransactionType transactionType, final Object element) {
439 getOperationalState().getDeviceInfo().addToControllerTx(transactionType, element);
442 public <T> HwvtepDeviceInfo.DeviceData fetchDeviceData(final Class<? extends Identifiable> cls,
443 final InstanceIdentifier key) {
444 HwvtepDeviceInfo.DeviceData deviceData = getDeviceOpData(cls, key);
445 if (deviceData == null) {
446 LOG.debug("Could not find data for key {}", getNodeKeyStr(key));
447 Optional<TypedBaseTable> optional = getTableReader().getHwvtepTableEntryUUID(cls, key, null);
448 if (optional.isPresent()) {
449 TypedBaseTable table = optional.orElseThrow();
450 LOG.debug("Found the data for key from device {} ", getNodeKeyStr(key));
451 getDeviceInfo().updateDeviceOperData(cls, key, table.getUuid(), table);
452 return getDeviceOpData(cls, key);
454 LOG.info("Could not Find the data for key from device {} ", getNodeKeyStr(key));
460 public <K extends Identifiable> void addJobToQueue(final DependentJob<K> job) {
461 hwvtepOperationalState.getDeviceInfo().putKeyInDependencyQueue(job.getKey());
462 hwvtepOperationalState.getDeviceInfo().addJobToQueue(job);
465 public void markKeyAsInTransit(final Class<? extends Identifiable> cls, final InstanceIdentifier key) {
466 hwvtepOperationalState.getDeviceInfo().markKeyAsInTransit(cls, key);
469 public HwvtepDeviceInfo.DeviceData getDeviceOpData(final Class<? extends Identifiable> cls,
470 final InstanceIdentifier key) {
471 return getOperationalState().getDeviceInfo().getDeviceOperData(cls, key);
474 public void clearConfigData(final Class<? extends Identifiable> cls, final InstanceIdentifier key) {
475 hwvtepOperationalState.getDeviceInfo().clearConfigData(cls, key);
478 public HwvtepDeviceInfo.DeviceData getConfigData(final Class<? extends Identifiable> cls,
479 final InstanceIdentifier key) {
480 return hwvtepOperationalState.getDeviceInfo().getConfigData(cls, key);
483 public void updateConfigData(final Class<? extends Identifiable> cls, final InstanceIdentifier key,
485 hwvtepOperationalState.getDeviceInfo().updateConfigData(cls, key, data);
488 @SuppressWarnings("checkstyle:IllegalCatch")
489 public AbstractTransactCommand getClone() {
491 return getClass().getConstructor(HwvtepOperationalState.class, Collection.class)
492 .newInstance(hwvtepOperationalState, changes);
493 } catch (Throwable e) {
494 LOG.error("Failed to clone the cmd ", e);
499 public HwvtepTableReader getTableReader() {
500 return getOperationalState().getConnectionInstance().getHwvtepTableReader();
503 public HwvtepConnectionInstance getConnectionInstance() {
504 return hwvtepOperationalState.getConnectionInstance();
507 public HwvtepOperationalState newOperState() {
508 return new HwvtepOperationalState(getConnectionInstance());
511 protected String getNodeKeyStr(final InstanceIdentifier<T> iid) {
512 return getClassType().getTypeName() + "."
513 + iid.firstKeyOf(Node.class).getNodeId().getValue() + "." + getKeyStr(iid);
516 protected String getKeyStr(final InstanceIdentifier<T> iid) {
517 return iid.toString();
520 protected String getLsKeyStr(final InstanceIdentifier iid) {
521 return ((InstanceIdentifier<LogicalSwitches>)iid).firstKeyOf(LogicalSwitches.class)
522 .getHwvtepNodeName().getValue();