/*
* Copyright © 2015, 2017 China Telecom Beijing Research Institute and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil;
import org.opendaylight.ovsdb.lib.notation.UUID;
import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.Identifiable;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
public abstract class AbstractTransactCommand<T extends Identifiable, Aug extends Augmentation<Node>> implements TransactCommand<T> {
private HwvtepOperationalState operationalState;
private Collection<DataTreeModification<Node>> changes;
protected AbstractTransactCommand() {
// NO OP
}
public AbstractTransactCommand(HwvtepOperationalState state, Collection<DataTreeModification<Node>> changes) {
this.operationalState = state;
this.changes = changes;
}
public HwvtepOperationalState getOperationalState() {
return operationalState;
}
public Collection<DataTreeModification<Node>> getChanges() {
return changes;
}
void updateCurrentTxDeleteData(Class<? extends Identifiable> cls, InstanceIdentifier key, T data) {
operationalState.updateCurrentTxDeleteData(cls, key);
operationalState.getDeviceInfo().clearConfigData(cls, key);
}
void updateCurrentTxData(Class<? extends Identifiable> cls, InstanceIdentifier key, UUID uuid, Object data) {
operationalState.updateCurrentTxData(cls, key, uuid);
operationalState.getDeviceInfo().markKeyAsInTransit(cls, key);
operationalState.getDeviceInfo().updateConfigData(cls, key, data);
}
void processDependencies(UnMetDependencyGetter<T> unMetDependencyGetter,
TransactionBuilder transaction,
final InstanceIdentifier<Node> nodeIid,
final InstanceIdentifier key,
final T data, final Object... extraData) {
HwvtepDeviceInfo deviceInfo = operationalState.getDeviceInfo();
Map inTransitDependencies = unMetDependencyGetter.getInTransitDependencies(operationalState, data);
Map confingDependencies = unMetDependencyGetter.getUnMetConfigDependencies(operationalState, data);
//we can skip the config termination point dependency as we can create them in device as part of this tx
confingDependencies.remove(TerminationPoint.class);
Type type = getClass().getGenericSuperclass();
Type classType = ((ParameterizedType)type).getActualTypeArguments()[0];
//If this key itself is in transit wait for the response of this key itself
if (deviceInfo.isKeyInTransit((Class<? extends Identifiable>) classType, key)) {
inTransitDependencies.put(classType, Collections.singletonList(key));
}
if (HwvtepSouthboundUtil.isEmptyMap(confingDependencies) && HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
doDeviceTransaction(transaction, nodeIid, data, key, extraData);
//TODO put proper uuid
updateCurrentTxData((Class<? extends Identifiable>) classType, key, new UUID("uuid"), data);
}
if (!HwvtepSouthboundUtil.isEmptyMap(confingDependencies)) {
DependentJob<T> configWaitingJob = new DependentJob.ConfigWaitingJob(
key, data, confingDependencies) {
@Override
public void onDependencyResolved(HwvtepOperationalState operationalState,
TransactionBuilder transactionBuilder) {
AbstractTransactCommand.this.operationalState = operationalState;
onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
}
};
deviceInfo.addJobToQueue(configWaitingJob);
}
if (!HwvtepSouthboundUtil.isEmptyMap(inTransitDependencies)) {
DependentJob<T> opWaitingJob = new DependentJob.OpWaitingJob(
key, data, inTransitDependencies) {
@Override
public void onDependencyResolved(HwvtepOperationalState operationalState,
TransactionBuilder transactionBuilder) {
AbstractTransactCommand.this.operationalState = operationalState;
onConfigUpdate(transactionBuilder, nodeIid, data, key, extraData);
}
};
deviceInfo.addJobToQueue(opWaitingJob);
}
}
public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
InstanceIdentifier key, Object... extraData) {
//tobe removed as part of refactoring patch
}
public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data,
InstanceIdentifier key, Object... extraData) {
//tobe removed as part of refactoring patch
}
protected Aug getAugmentation(Node node) {
if (node == null) {
return null;
}
ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
Class<? extends Augmentation<Node>> augType = (Class<? extends Augmentation<Node>>) parameterizedType.getActualTypeArguments()[1];
Augmentation<Node> augmentation = node.getAugmentation(augType);
return (Aug)augmentation;
}
protected List<T> getData(Aug augmentation) {
return Collections.EMPTY_LIST;
}
protected List<T> getData(Node node) {
Aug augmentation = getAugmentation(node);
if (augmentation != null) {
List<T> data = getData(augmentation);
if (data != null) {
return new ArrayList<>(data);
}
}
return Collections.emptyList();
}
protected Map<InstanceIdentifier<Node>, List<T>> extractRemoved(
Collection<DataTreeModification<Node>> changes, Class<T> class1) {
Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
if (changes != null && !changes.isEmpty()) {
for (DataTreeModification<Node> change : changes) {
final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
List<T> removed;
if (operationalState.isInReconciliation()) {
removed = getRemoved(change);
} else {
removed = (List<T>) operationalState.getDeletedData(key, classType);
}
removed.addAll(getCascadeDeleteData(change));
result.put(key, removed);
}
}
return result;
}
protected Map<InstanceIdentifier<Node>, List<T>> extractUpdated(
Collection<DataTreeModification<Node>> changes, Class<T> class1) {
Map<InstanceIdentifier<Node>, List<T>> result = new HashMap<>();
if (changes != null && !changes.isEmpty()) {
for (DataTreeModification<Node> change : changes) {
InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
Class<? extends Identifiable> classType = (Class<? extends Identifiable>) getClassType();
List<T> updated = null;
if (operationalState.isInReconciliation()) {
updated = getUpdated(change);
} else {
updated = (List<T>) operationalState.getUpdatedData(key, classType);
}
result.put(key, updated);
}
}
return result;
}
List<T> getCascadeDeleteData(DataTreeModification<Node> change) {
if (!cascadeDelete()) {
return Collections.EMPTY_LIST;
}
DataObjectModification<Node> mod = change.getRootNode();
Node updatedNode = TransactUtils.getUpdated(mod);
List<T> updatedData = getData(updatedNode);
Set<InstanceIdentifier> deleted = getOperationalState().getDeletedKeysInCurrentTx(LogicalSwitches.class);
UnMetDependencyGetter dependencyGetter = getDependencyGetter();
if (!HwvtepSouthboundUtil.isEmpty(deleted) && !HwvtepSouthboundUtil.isEmpty(updatedData) && dependencyGetter != null) {
List<T> removed = new ArrayList<>();
for (T ele : updatedData) {
if (deleted.containsAll(dependencyGetter.getLogicalSwitchDependencies(ele))) {
removed.add(ele);
}
}
return removed;
}
return Collections.EMPTY_LIST;
}
List<T> getRemoved(DataTreeModification<Node> change) {
DataObjectModification<Node> mod = change.getRootNode();
Node removed = TransactUtils.getRemoved(mod);
Node updated = TransactUtils.getUpdated(mod);
Node before = mod.getDataBefore();
return diffOf(removed, before, updated, true);
}
List<T> getUpdated(DataTreeModification<Node> change) {
DataObjectModification<Node> mod = change.getRootNode();
Node updated = TransactUtils.getUpdated(mod);
Node before = mod.getDataBefore();
return diffOf(updated, before, false);
}
List<T> diffOf(Node include, Node a, Node b, boolean compareKeyOnly) {
List<T> data1 = getData(include);
List<T> data2 = diffOf(a, b, compareKeyOnly);
if (HwvtepSouthboundUtil.isEmpty(data1) && HwvtepSouthboundUtil.isEmpty(data2)) {
return Collections.emptyList();
}
List<T> result = new ArrayList<>(data1);
result.addAll(data2);
return result;
}
List<T> diffOf(Node a, Node b, boolean compareKeyOnly) {
List<T> result = new ArrayList<>();
List<T> list1 = getData(a);
List<T> list2 = getData(b);
if (HwvtepSouthboundUtil.isEmpty(list1)) {
return Collections.emptyList();
}
if (HwvtepSouthboundUtil.isEmpty(list2)) {
return HwvtepSouthboundUtil.isEmpty(list1) ? Collections.emptyList() : list1;
}
Iterator<T> it1 = list1.iterator();
while(it1.hasNext()) {
T ele = it1.next();
Iterator<T> it2 = list2.iterator();
boolean found = false;
while (it2.hasNext()) {
T other = it2.next();
found = compareKeyOnly ? Objects.equals(ele.getKey(), other.getKey()) : areEqual(ele, other);
if ( found ) {
it2.remove();
break;
}
}
if (!found) {
result.add(ele);
}
}
return result;
}
protected Type getClassType() {
Type type = getClass().getGenericSuperclass();
Type classType = ((ParameterizedType)type).getActualTypeArguments()[0];
return classType;
}
protected boolean areEqual(T a , T b) {
return a.getKey().equals(b.getKey());
}
protected UnMetDependencyGetter getDependencyGetter() {
return null;
}
/**
* Tells if this object needs to be deleted if its dependent object gets deleted
* Ex : LocalUcastMac and LocalMacstMac
* @return true if this object needs to be deleted if its dependent object gets deleted
*/
protected boolean cascadeDelete() {
return false;
}
}