bug 6579 added dependency queue 50/48850/6
authorK.V Suneelu Verma <k.v.suneelu.verma@ericsson.com>
Thu, 1 Dec 2016 07:11:23 +0000 (12:41 +0530)
committerK.V Suneelu Verma <k.v.suneelu.verma@ericsson.com>
Tue, 13 Dec 2016 09:44:39 +0000 (15:14 +0530)
Added Dependency Queue which gets processed upon config and op events.
Any object ( mac / port ) whose dependencies are not met gets pushed the
queue with an expiry timeout.

Added the following apis in UnMetDependencyGetter
getInTransitDependencies    returns list of keys in json rpc transaction
which are needed by this object to be processed.
getUnMetConfigDependencies returns list of missing config keys

Added the following apis in HwvtepDeviceInfo
onConfigDataAvailable processes config dependency queue
onOpDataAvailable     processes in transit dependency queue , gets called
after every json rpc transaction is done

Change-Id: I012958a40e0ab0aa0e0db14dc670f9c4dacbdd09
Signed-off-by: K.V Suneelu Verma <k.v.suneelu.verma@ericsson.com>
22 files changed:
hwvtepsouthbound/hwvtepsouthbound-impl/pom.xml
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepConnectionManager.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepDataChangeListener.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepDeviceInfo.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepSouthboundConstants.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepSouthboundUtil.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/reconciliation/configuration/HwvtepReconciliationTask.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/AbstractTransactCommand.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/DependencyQueue.java [new file with mode: 0644]
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/DependentJob.java [new file with mode: 0644]
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/HwvtepOperationalState.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/LogicalSwitchUpdateCommand.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/McastMacsRemoteUpdateCommand.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/TransactCommand.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/TransactCommandAggregator.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/UcastMacsRemoteUpdateCommand.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/UnMetDependencyGetter.java [new file with mode: 0644]
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transactions/md/HwvtepOperationalCommandAggregator.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/test/java/org/opendaylight/ovsdb/hwvtepsouthbound/DataChangeListenerTestBase.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/test/java/org/opendaylight/ovsdb/hwvtepsouthbound/DependencyQueueTest.java [new file with mode: 0644]
hwvtepsouthbound/hwvtepsouthbound-impl/src/test/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepOperationalStateTest.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/test/java/org/opendaylight/ovsdb/hwvtepsouthbound/UnMetDependencyGetterTest.java [new file with mode: 0644]

index d8d647c34ee5d7ee3b5101260cf77ac88ba10956..bbb24bde9115576398db766197dba9b5b50df1e3 100644 (file)
@@ -101,6 +101,14 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
           <failsOnError>true</failsOnError>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <forkCount>1</forkCount>
+          <reuseForks>false</reuseForks>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
 
index b40a7367cc691544c9741d1a5add0fc9fe33bc05..835d435d917f0f3a739ad8e36f5fa5a220b7af7d 100644 (file)
@@ -8,22 +8,11 @@
 
 package org.opendaylight.ovsdb.hwvtepsouthbound;
 
-import static org.opendaylight.ovsdb.lib.operations.Operations.op;
-
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
@@ -41,6 +30,7 @@ import org.opendaylight.ovsdb.hwvtepsouthbound.reconciliation.ReconciliationMana
 import org.opendaylight.ovsdb.hwvtepsouthbound.reconciliation.ReconciliationTask;
 import org.opendaylight.ovsdb.hwvtepsouthbound.reconciliation.configuration.HwvtepReconciliationTask;
 import org.opendaylight.ovsdb.hwvtepsouthbound.reconciliation.connection.ConnectionReconciliationTask;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.DependencyQueue;
 import org.opendaylight.ovsdb.hwvtepsouthbound.transactions.md.HwvtepGlobalRemoveCommand;
 import org.opendaylight.ovsdb.hwvtepsouthbound.transactions.md.TransactionCommand;
 import org.opendaylight.ovsdb.hwvtepsouthbound.transactions.md.TransactionInvoker;
@@ -54,7 +44,6 @@ import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
 import org.opendaylight.ovsdb.lib.schema.typed.TyperUtils;
 import org.opendaylight.ovsdb.schema.hardwarevtep.Global;
-import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalSwitchAttributes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
@@ -65,11 +54,20 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static org.opendaylight.ovsdb.lib.operations.Operations.op;
 
 public class HwvtepConnectionManager implements OvsdbConnectionListener, AutoCloseable{
     private Map<ConnectionInfo, HwvtepConnectionInstance> clients = new ConcurrentHashMap<>();
@@ -103,6 +101,7 @@ public class HwvtepConnectionManager implements OvsdbConnectionListener, AutoClo
         for (HwvtepConnectionInstance client: clients.values()) {
             client.disconnect();
         }
+        DependencyQueue.close();
     }
 
     @Override
index 5cca754d4b009ab573164316b20c812ae5e8c2a5..f5d96e38632919e307d6466d23f74624c43b0183 100644 (file)
@@ -184,7 +184,8 @@ public class HwvtepDataChangeListener implements ClusteredDataTreeChangeListener
                 changesByConnectionInstance(changes).entrySet()) {
             HwvtepConnectionInstance connectionInstance = changesEntry.getKey();
             connectionInstance.transact(new TransactCommandAggregator(
-                new HwvtepOperationalState(db, changesEntry.getValue()),changesEntry.getValue()));
+                new HwvtepOperationalState(db, connectionInstance, changesEntry.getValue()),changesEntry.getValue()));
+            connectionInstance.getDeviceInfo().onConfigDataAvailable();
         }
     }
 
index d4dba02b05ad072d6c0e4b27bd1da3f4aa030d3f..77725d7e914bf73431f48583bc984a252b9c5579 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.ovsdb.hwvtepsouthbound;
 
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.DependencyQueue;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.DependentJob;
 import org.opendaylight.ovsdb.lib.notation.UUID;
 import org.opendaylight.ovsdb.schema.hardwarevtep.LogicalSwitch;
 import org.opendaylight.ovsdb.schema.hardwarevtep.PhysicalLocator;
@@ -55,7 +57,7 @@ public class HwvtepDeviceInfo {
         private final Object data;
         private final DeviceDataStatus status;
 
-        public DeviceData(InstanceIdentifier key, UUID uuid, Object data, DeviceDataStatus status) {
+        DeviceData(InstanceIdentifier key, UUID uuid, Object data, DeviceDataStatus status) {
             this.data = data;
             this.key = key;
             this.status = status;
@@ -86,6 +88,7 @@ public class HwvtepDeviceInfo {
     private Map<Class<? extends Identifiable>, Map<InstanceIdentifier, DeviceData>> configKeyVsData = new ConcurrentHashMap<>();
     private Map<Class<? extends Identifiable>, Map<InstanceIdentifier, DeviceData>> opKeyVsData = new ConcurrentHashMap<>();
     private Map<Class<? extends Identifiable>, Map<UUID, Object>> uuidVsData = new ConcurrentHashMap<>();
+    private DependencyQueue dependencyQueue;
 
     public HwvtepDeviceInfo(HwvtepConnectionInstance hwvtepConnectionInstance) {
         this.connectionInstance = hwvtepConnectionInstance;
@@ -93,6 +96,7 @@ public class HwvtepDeviceInfo {
         this.physicalSwitches = new HashMap<>();
         this.physicalLocators = new HashMap<>();
         this.mapTunnelToPhysicalSwitch = new HashMap<>();
+        this.dependencyQueue = new DependencyQueue(this);
     }
 
     public void putLogicalSwitch(UUID uuid, LogicalSwitch lSwitch) {
@@ -211,4 +215,16 @@ public class HwvtepDeviceInfo {
         }
         return null;
     }
+
+    public <T extends Identifiable> void addJobToQueue(DependentJob<T> job) {
+        dependencyQueue.addToQueue(job);
+    }
+
+    public void onConfigDataAvailable() {
+        dependencyQueue.processReadyJobsFromConfigQueue(connectionInstance);
+    }
+
+    public void onOpDataAvailable() {
+        dependencyQueue.processReadyJobsFromOpQueue(connectionInstance);
+    }
 }
index d594115e8eabd044ea3026bb9d1a6272a0db06dd..547b70a32a11eb33edbd1edd68362ec084463134 100644 (file)
@@ -44,4 +44,6 @@ public class HwvtepSouthboundConstants {
             = new ImmutableMap.Builder<String,List<String>>()
             .put("Manager", Arrays.asList(new String[]{"_version", "status"}))
             .build();
+    public static final int WAITING_QUEUE_CAPACITY = 1000;
+    public static final long WAITING_JOB_EXPIRY_TIME_MILLIS = 90000;
 }
index f6300ec9c371d7b31c488a89aee8b49ef8e1cc8e..eb29948f9647b041669a1f0311c05e7a7f9cfc1d 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -181,4 +182,8 @@ public class HwvtepSouthboundUtil {
             map.get(cls).remove(key);
         }
     }
+
+    public static <T> boolean isEmpty(List<T> list) {
+        return list == null || list.isEmpty();
+    }
 }
index cd9a8172da72a7dc47e195ec4a32e713c87ba6df..a8adf4cf4e483666147cd71b7f550d0d779ee335 100644 (file)
@@ -57,7 +57,7 @@ public class HwvtepReconciliationTask extends ReconciliationTask {
     }
 
     private void transactChangesToDevice(Collection<DataTreeModification<Node>> changes) {
-        HwvtepOperationalState hwvtepOperationalState = new HwvtepOperationalState(db, changes);
+        HwvtepOperationalState hwvtepOperationalState = new HwvtepOperationalState(db, connectionInstance, changes);
         connectionInstance.transact(new TransactCommandAggregator(hwvtepOperationalState,changes));
     }
 
index d766f199f246e02a6ab71738f0355b526865cb6b..f7fa3b40bc48966913961954f985ac7710d5d8ad 100644 (file)
@@ -8,12 +8,19 @@
 
 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
 
-import java.util.Collection;
-
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
+import org.opendaylight.ovsdb.lib.notation.UUID;
+import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
 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.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.Collection;
+import java.util.Map;
 
-public abstract class AbstractTransactCommand implements TransactCommand {
+public abstract class AbstractTransactCommand<T extends Identifiable> implements TransactCommand<T> {
 
     private HwvtepOperationalState operationalState;
     private Collection<DataTreeModification<Node>> changes;
@@ -34,4 +41,61 @@ public abstract class AbstractTransactCommand implements TransactCommand {
     public Collection<DataTreeModification<Node>> getChanges() {
         return changes;
     }
+
+    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) {
+
+        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);
+
+        if (confingDependencies.isEmpty() && inTransitDependencies.isEmpty()) {
+            doDeviceTransaction(transaction, nodeIid, data);
+        }
+        if (!confingDependencies.isEmpty()) {
+            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);
+                }
+            };
+            deviceInfo.addJobToQueue(configWaitingJob);
+        }
+        if (inTransitDependencies.size() > 0) {
+            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);
+                }
+            };
+            deviceInfo.addJobToQueue(opWaitingJob);
+        }
+    }
+
+    public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data) {
+        //tobe removed as part of refactoring patch
+    }
+
+    public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data) {
+        //tobe removed as part of refactoring patch
+    }
 }
diff --git a/hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/DependencyQueue.java b/hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/DependencyQueue.java
new file mode 100644 (file)
index 0000000..314ab56
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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 com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepConnectionInstance;
+import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
+import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundConstants;
+import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+
+public class DependencyQueue {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DependencyQueue.class);
+    private static final ThreadFactory threadFact = new ThreadFactoryBuilder().setNameFormat("hwvtep-waiting-job-%d").
+            build();
+    private static final ExecutorService executorService = Executors.newSingleThreadScheduledExecutor(threadFact);
+
+    private final LinkedBlockingQueue<DependentJob> configWaitQueue = new LinkedBlockingQueue<>(
+            HwvtepSouthboundConstants.WAITING_QUEUE_CAPACITY);
+    private final LinkedBlockingQueue<DependentJob> opWaitQueue = new LinkedBlockingQueue<>(
+            HwvtepSouthboundConstants.WAITING_QUEUE_CAPACITY);
+    private final HwvtepDeviceInfo deviceInfo;
+
+    public DependencyQueue(HwvtepDeviceInfo hwvtepDeviceInfo) {
+        this.deviceInfo = hwvtepDeviceInfo;
+    }
+
+    /**
+     * Tries to add the job to the waiting queue
+     * @param waitingJob The job to be enqueued
+     * @return true if it is successfully added to the queue
+     */
+    public boolean addToQueue(DependentJob waitingJob) {
+        boolean addedToQueue;
+        if (waitingJob instanceof DependentJob.ConfigWaitingJob) {
+            addedToQueue = configWaitQueue.offer(waitingJob);
+        } else {
+            addedToQueue = opWaitQueue.offer(waitingJob);
+        }
+        if (addedToQueue) {
+            LOG.debug("Added the waiting job {} to queue", waitingJob.getKey());
+        } else {
+            LOG.error("Failed to add the waiting job to queue {}", waitingJob.getKey());
+        }
+        return addedToQueue;
+    }
+
+    /**
+     * Checks if any config data dependent jobs are ready to be processed and process them
+     * @param connectionInstance The connection instance
+     */
+    public void processReadyJobsFromConfigQueue(HwvtepConnectionInstance connectionInstance) {
+        processReadyJobs(connectionInstance, configWaitQueue);
+    }
+
+    /**
+     * Checks if any operational data dependent jobs are ready to be processed and process them
+     * @param connectionInstance The connection instance
+     */
+    public void processReadyJobsFromOpQueue(HwvtepConnectionInstance connectionInstance) {
+        processReadyJobs(connectionInstance, opWaitQueue);
+    }
+
+    private void processReadyJobs(final HwvtepConnectionInstance hwvtepConnectionInstance,
+                                  LinkedBlockingQueue<DependentJob> queue) {
+        final List<DependentJob> readyJobs =  getReadyJobs(queue);
+        if (readyJobs.size() > 0) {
+            executorService.submit(new Runnable() {
+                @Override
+                public void run() {
+                    hwvtepConnectionInstance.transact(new TransactCommand() {
+                        @Override
+                        public void execute(TransactionBuilder transactionBuilder) {
+                            HwvtepOperationalState operationalState = new HwvtepOperationalState(hwvtepConnectionInstance);
+                            for (DependentJob job : readyJobs) {
+                                job.onDependencyResolved(operationalState, transactionBuilder);
+                            }
+                        }
+
+                        @Override
+                        public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier nodeIid,
+                                                   Identifiable data) {
+                        }
+
+                        @Override
+                        public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier nodeIid,
+                                                        Identifiable data) {
+                        }
+                    });
+                }
+            });
+        }
+    }
+
+    private List<DependentJob> getReadyJobs(LinkedBlockingQueue<DependentJob> queue) {
+        List<DependentJob> readyJobs = new ArrayList<>();
+        Iterator<DependentJob> jobIterator = queue.iterator();
+        while(jobIterator.hasNext()) {
+            DependentJob job = jobIterator.next();
+            long currentTime = System.currentTimeMillis();
+            if (job.isExpired(currentTime)) {
+                jobIterator.remove();
+                continue;
+            }
+            if (job.areDependenciesMet(deviceInfo)) {
+                jobIterator.remove();
+                readyJobs.add(job);
+            }
+        }
+        return readyJobs;
+    }
+
+    public static void close() {
+        executorService.shutdown();
+    }
+}
\ No newline at end of file
diff --git a/hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/DependentJob.java b/hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/DependentJob.java
new file mode 100644 (file)
index 0000000..68dc795
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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 org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
+import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundConstants;
+import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.List;
+import java.util.Map;
+
+public abstract class DependentJob<T extends Identifiable> {
+
+    private final long expiryTime;
+    private final InstanceIdentifier key;
+    private final T data;
+    private final Map<Class<? extends DataObject>, List<InstanceIdentifier>> dependencies;
+
+    DependentJob(InstanceIdentifier key,
+                           T data, Map<Class<? extends DataObject>, List<InstanceIdentifier>> dependencies) {
+        this.expiryTime = System.currentTimeMillis() + HwvtepSouthboundConstants.WAITING_JOB_EXPIRY_TIME_MILLIS;
+        this.key = key;
+        this.data = data;
+        this.dependencies = dependencies;
+    }
+
+    /**
+     * This call back method gets called when all its dependencies are resolved
+     * @param operationalState   new current operational state
+     * @param transactionBuilder transaction builder to create device transaction
+     */
+    protected abstract void onDependencyResolved(HwvtepOperationalState operationalState,
+                                                 TransactionBuilder transactionBuilder);
+
+    /**
+     * This method is to check if all the given dependency of this job or not
+     * @param deviceInfo   The device info of tis job
+     * @param cls          dependency type to be checked for
+     * @param iid          instance identifier to be checked for
+     * @return true if the dependency is met
+     */
+    protected abstract boolean isDependencyMet(HwvtepDeviceInfo deviceInfo, Class<? extends DataObject> cls,
+                                               InstanceIdentifier iid);
+
+    boolean isExpired(long currentTime) {
+        return currentTime > expiryTime;
+    }
+
+    /**
+     * This method checks if all the dependencies of this job or met or not
+     * @param deviceInfo The device info of this job
+     * @return true if all the dependencies are met
+     */
+    boolean areDependenciesMet(HwvtepDeviceInfo deviceInfo) {
+        for (Class<? extends DataObject> cls : dependencies.keySet()) {
+            for (InstanceIdentifier iid : dependencies.get(cls)) {
+                if (!isDependencyMet(deviceInfo, cls, iid)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public InstanceIdentifier getKey() {
+        return key;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public abstract static class ConfigWaitingJob<T extends Identifiable> extends DependentJob {
+
+        public ConfigWaitingJob(InstanceIdentifier key, T data, Map dependencies) {
+            super(key, data, dependencies);
+        }
+
+        @Override
+        protected boolean isDependencyMet(HwvtepDeviceInfo deviceInfo, Class cls, InstanceIdentifier iid) {
+            return deviceInfo.isConfigDataAvailable(cls, iid);
+        }
+    }
+
+    public abstract static class OpWaitingJob<T extends Identifiable> extends DependentJob {
+
+        public OpWaitingJob(InstanceIdentifier key, T data, Map dependencies) {
+            super(key, data, dependencies);
+        }
+
+        @Override
+        protected boolean isDependencyMet(HwvtepDeviceInfo deviceInfo, Class cls, InstanceIdentifier iid) {
+            HwvtepDeviceInfo.DeviceData deviceData = deviceInfo.getDeviceOpData(cls, iid);
+            return deviceData == null || deviceData.getStatus() != HwvtepDeviceInfo.DeviceDataStatus.IN_TRANSIT;
+        }
+    }
+}
index efecef985d66dfc4b8912550575eea8ed7bc2fe2..2c15a506a6a33249bc67c3f827365b35b16dccad 100644 (file)
@@ -67,7 +67,10 @@ public class HwvtepOperationalState {
     private Map<Class<? extends Identifiable>, Map<InstanceIdentifier, UUID>> currentTxUUIDs = new ConcurrentHashMap<>();
     private Map<Class<? extends Identifiable>, Map<InstanceIdentifier, Boolean>> currentTxDeletedKeys = new ConcurrentHashMap<>();
 
-    public HwvtepOperationalState(DataBroker db, Collection<DataTreeModification<Node>> changes) {
+    public HwvtepOperationalState(DataBroker db, HwvtepConnectionInstance connectionInstance,
+                                  Collection<DataTreeModification<Node>> changes) {
+        this.connectionInstance = connectionInstance;
+        this.deviceInfo = connectionInstance.getDeviceInfo();
         Map<InstanceIdentifier<Node>, Node> nodeCreateOrUpdate =
             TransactUtils.extractCreatedOrUpdatedOrRemoved(changes, Node.class);
         if (nodeCreateOrUpdate != null) {
@@ -340,7 +343,6 @@ public class HwvtepOperationalState {
 
     public void updateCurrentTxData(Class<? extends Identifiable> cls, InstanceIdentifier key, UUID uuid) {
         HwvtepSouthboundUtil.updateData(currentTxUUIDs, cls, key, uuid);
-        deviceInfo.markKeyAsInTransit(cls, key);
     }
 
     public void updateCurrentTxDeleteData(Class<? extends Identifiable> cls, InstanceIdentifier key) {
index bbde50e671ea8840f8779e72a957c13f1c2876e8..a0068d40e21cfc390fcf564f5d4623a291e4cc61 100644 (file)
@@ -20,6 +20,8 @@ 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.HwvtepSouthboundUtil;
+import org.opendaylight.ovsdb.lib.notation.UUID;
 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
 import org.opendaylight.ovsdb.lib.schema.typed.TyperUtils;
 import org.opendaylight.ovsdb.schema.hardwarevtep.LogicalSwitch;
@@ -63,6 +65,8 @@ public class LogicalSwitchUpdateCommand extends AbstractTransactCommand {
     private void updateLogicalSwitch(TransactionBuilder transaction,
             InstanceIdentifier<Node> instanceIdentifier, List<LogicalSwitches> lswitchList) {
         for (LogicalSwitches lswitch: lswitchList) {
+            InstanceIdentifier<LogicalSwitches> lsKey = instanceIdentifier.
+                    augmentation(HwvtepGlobalAugmentation.class).child(LogicalSwitches.class, lswitch.getKey());
             LOG.debug("Creating logcial switch named: {}", lswitch.getHwvtepNodeName());
             Optional<LogicalSwitches> operationalSwitchOptional =
                     getOperationalState().getLogicalSwitches(instanceIdentifier, lswitch.getKey());
@@ -86,6 +90,8 @@ public class LogicalSwitchUpdateCommand extends AbstractTransactCommand {
                         .build());
                 transaction.add(op.comment("Logical Switch: Updating " + existingLogicalSwitchName));
             }
+            UUID lsUuid = new UUID(TransactUtils.getLogicalSwitchId(lswitch));
+            updateCurrentTxData(LogicalSwitches.class, lsKey, lsUuid, lswitch);
         }
     }
 
index d53171df24f3b66852a580af720c9e550bbfae68..f29ccfb845332bab46097e4e2391729e6a6d8245 100644 (file)
@@ -8,17 +8,12 @@
 
 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
 
-import static org.opendaylight.ovsdb.lib.operations.Operations.op;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundConstants;
+import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepSouthboundUtil;
 import org.opendaylight.ovsdb.lib.notation.UUID;
 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
 import org.opendaylight.ovsdb.lib.schema.typed.TyperUtils;
@@ -27,15 +22,25 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
 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.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import static org.opendaylight.ovsdb.lib.operations.Operations.op;
 
 public class McastMacsRemoteUpdateCommand extends AbstractTransactCommand {
     private static final Logger LOG = LoggerFactory.getLogger(McastMacsRemoteUpdateCommand.class);
+    private static final McastMacUnMetDependencyGetter MCAST_MAC_DATA_VALIDATOR = new McastMacUnMetDependencyGetter();
 
     public McastMacsRemoteUpdateCommand(HwvtepOperationalState state,
             Collection<DataTreeModification<Node>> changes) {
@@ -192,4 +197,25 @@ public class McastMacsRemoteUpdateCommand extends AbstractTransactCommand {
         }
         return result;
     }
+
+    static class McastMacUnMetDependencyGetter extends UnMetDependencyGetter<RemoteMcastMacs> {
+
+        public List<InstanceIdentifier<?>> getLogicalSwitchDependencies(RemoteMcastMacs data) {
+            if (data == null) {
+                return Collections.EMPTY_LIST;
+            }
+            return Lists.newArrayList(data.getLogicalSwitchRef().getValue());
+        }
+
+        public List<InstanceIdentifier<?>> getTerminationPointDependencies(RemoteMcastMacs data) {
+            if (data == null || HwvtepSouthboundUtil.isEmpty(data.getLocatorSet())) {
+                return Collections.EMPTY_LIST;
+            }
+            List<InstanceIdentifier<?>> locators = new ArrayList<>();
+            for (LocatorSet locator: data.getLocatorSet()) {
+                locators.add(locator.getLocatorRef().getValue());
+            }
+            return locators;
+        }
+    }
 }
index ac9eeea8f73db9f8421b01b3af1da2ac224a0a07..8a7771606d1da860a2f5e49f66feda754302401d 100644 (file)
@@ -9,9 +9,15 @@
 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
 
 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
-public interface TransactCommand {
+public interface TransactCommand<T extends Identifiable> {
 
     void execute(TransactionBuilder transaction);
 
+    void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data);
+
+    void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier<Node> nodeIid, T data);
 }
index cd1c79c864e464134dcd67452eb4496bd52eeb93..5d894fa8ba391c8003726b968671cd59652a6dc1 100644 (file)
@@ -8,13 +8,15 @@
 
 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 public class TransactCommandAggregator implements TransactCommand {
 
@@ -45,4 +47,12 @@ public class TransactCommandAggregator implements TransactCommand {
             command.execute(transaction);
         }
     }
+
+    @Override
+    public void onConfigUpdate(TransactionBuilder transaction, InstanceIdentifier nodeIid, Identifiable data) {
+    }
+
+    @Override
+    public void doDeviceTransaction(TransactionBuilder transaction, InstanceIdentifier nodeIid, Identifiable data) {
+    }
 }
index 3a2758d22c8b43b199fbd28cb47d624c33cf8823..e26ec486bac9b8401bca4663c68c1e9c88936387 100644 (file)
@@ -8,14 +8,8 @@
 
 package org.opendaylight.ovsdb.hwvtepsouthbound.transact;
 
-import static org.opendaylight.ovsdb.lib.operations.Operations.op;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.ovsdb.lib.notation.UUID;
@@ -27,16 +21,24 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hw
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalLocatorAugmentation;
 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.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacs;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
 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.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import static org.opendaylight.ovsdb.lib.operations.Operations.op;
 
-public class UcastMacsRemoteUpdateCommand extends AbstractTransactCommand {
+public class UcastMacsRemoteUpdateCommand extends AbstractTransactCommand<RemoteUcastMacs> {
     private static final Logger LOG = LoggerFactory.getLogger(UcastMacsRemoteUpdateCommand.class);
+    private static final UcastMacUnMetDependencyGetter UCAST_MAC_DATA_VALIDATOR = new UcastMacUnMetDependencyGetter();
 
     public UcastMacsRemoteUpdateCommand(HwvtepOperationalState state,
             Collection<DataTreeModification<Node>> changes) {
@@ -64,8 +66,30 @@ public class UcastMacsRemoteUpdateCommand extends AbstractTransactCommand {
     }
 
     private void updateUcastMacsRemote(TransactionBuilder transaction,
-            InstanceIdentifier<Node> instanceIdentifier, List<RemoteUcastMacs> remoteUcastMacs) {
-        for (RemoteUcastMacs remoteUcastMac: remoteUcastMacs) {
+                                       InstanceIdentifier<Node> instanceIdentifier,
+                                       List<RemoteUcastMacs> remoteUcastMacs) {
+        if (remoteUcastMacs == null) {
+            return;
+        }
+        for (RemoteUcastMacs remoteUcastMac : remoteUcastMacs) {
+            onConfigUpdate(transaction, instanceIdentifier, remoteUcastMac);
+        }
+    }
+
+    @Override
+    public void onConfigUpdate(TransactionBuilder transaction,
+                                  InstanceIdentifier<Node> nodeIid,
+                                  RemoteUcastMacs remoteUcastMacs) {
+        InstanceIdentifier<RemoteUcastMacs> macIid = nodeIid.augmentation(HwvtepGlobalAugmentation.class).
+                child(RemoteUcastMacs.class, remoteUcastMacs.getKey());
+        //TODO uncommet in next commit
+        //processDependencies(UCAST_MAC_DATA_VALIDATOR, transaction, nodeIid, macIid, remoteUcastMacs);
+        doDeviceTransaction(transaction, nodeIid, remoteUcastMacs);
+    }
+
+    @Override
+    public void doDeviceTransaction(TransactionBuilder transaction,
+                                       InstanceIdentifier<Node> instanceIdentifier, RemoteUcastMacs remoteUcastMac) {
             LOG.debug("Creating remoteUcastMacs, mac address: {}", remoteUcastMac.getMacEntryKey().getValue());
             Optional<RemoteUcastMacs> operationalMacOptional =
                     getOperationalState().getRemoteUcastMacs(instanceIdentifier, remoteUcastMac.getKey());
@@ -75,7 +99,7 @@ public class UcastMacsRemoteUpdateCommand extends AbstractTransactCommand {
             setLogicalSwitch(ucastMacsRemote, remoteUcastMac);
             if (!operationalMacOptional.isPresent()) {
                 setMac(ucastMacsRemote, remoteUcastMac, operationalMacOptional);
-                LOG.trace("execute: creating RemotUcastMac entry: {}", ucastMacsRemote);
+                LOG.trace("doDeviceTransaction: creating RemotUcastMac entry: {}", ucastMacsRemote);
                 transaction.add(op.insert(ucastMacsRemote));
                 transaction.add(op.comment("UcastMacRemote: Creating " + remoteUcastMac.getMacEntryKey().getValue()));
             } else if (operationalMacOptional.get().getMacEntryUuid() != null) {
@@ -83,7 +107,7 @@ public class UcastMacsRemoteUpdateCommand extends AbstractTransactCommand {
                 UcastMacsRemote extraMac = TyperUtils.getTypedRowWrapper(transaction.getDatabaseSchema(),
                                 UcastMacsRemote.class, null);
                 extraMac.getUuidColumn().setData(macEntryUUID);
-                LOG.trace("execute: updating RemotUcastMac entry: {}", ucastMacsRemote);
+                LOG.trace("doDeviceTransaction: updating RemotUcastMac entry: {}", ucastMacsRemote);
                 transaction.add(op.update(ucastMacsRemote)
                         .where(extraMac.getUuidColumn().getSchema().opEqual(macEntryUUID))
                         .build());
@@ -92,7 +116,6 @@ public class UcastMacsRemoteUpdateCommand extends AbstractTransactCommand {
                 LOG.warn("Unable to update remoteMcastMacs {} because uuid not found in the operational store",
                                 remoteUcastMac.getMacEntryKey().getValue());
             }
-        }
     }
 
     private void setLogicalSwitch(UcastMacsRemote ucastMacsRemote, RemoteUcastMacs inputMac) {
@@ -219,4 +242,21 @@ public class UcastMacsRemoteUpdateCommand extends AbstractTransactCommand {
         }
         return result;
     }
+
+    static class UcastMacUnMetDependencyGetter extends UnMetDependencyGetter<RemoteUcastMacs> {
+
+        public List<InstanceIdentifier<?>> getLogicalSwitchDependencies(RemoteUcastMacs data) {
+            if (data == null) {
+                return Collections.EMPTY_LIST;
+            }
+            return Lists.newArrayList(data.getLogicalSwitchRef().getValue());
+        }
+
+        public List<InstanceIdentifier<?>> getTerminationPointDependencies(RemoteUcastMacs data) {
+            if (data == null) {
+                return Collections.EMPTY_LIST;
+            }
+            return Lists.newArrayList(data.getLocatorRef().getValue());
+        }
+    }
 }
\ No newline at end of file
diff --git a/hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/UnMetDependencyGetter.java b/hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/transact/UnMetDependencyGetter.java
new file mode 100644 (file)
index 0000000..2141925
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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 org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.ovsdb.hwvtepsouthbound.HwvtepDeviceInfo;
+import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
+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.TerminationPoint;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class to retrieve the unmet dependencies (config/operational) of the given object
+ * @param <T>
+ */
+public abstract class UnMetDependencyGetter<T extends Identifiable> {
+
+    private final ConfigDependencyGetter configDependencyGetter = new ConfigDependencyGetter();
+    private final InTransitDependencyGetter inTransitDependencyGetter = new InTransitDependencyGetter();
+
+    /**
+     * Returns the iids this data depends upon
+     * which are already intransit in the previous transaction if any
+     * @param opState   The operatonal state
+     * @param data      The data object
+     * @return          The depenencies
+     */
+    public Map<Class<? extends Identifiable>, List<InstanceIdentifier>> getInTransitDependencies(
+            HwvtepOperationalState opState, T data) {
+        return inTransitDependencyGetter.retrieveUnMetDependencies(opState, opState.getDeviceInfo(), data);
+    }
+
+    /**
+     * Returns the iids this data depends upon
+     * which are not yet present in the config data store if any
+     * @param opState   The operatonal state
+     * @param data      The data object
+     * @return the      depenencies
+     */
+    public Map<Class<? extends Identifiable>, List<InstanceIdentifier>> getUnMetConfigDependencies(
+            HwvtepOperationalState opState, T data) {
+        return configDependencyGetter.retrieveUnMetDependencies(opState, opState.getDeviceInfo(), data);
+    }
+
+    abstract class DependencyGetter {
+
+        Map<Class<? extends Identifiable>, List<InstanceIdentifier>> retrieveUnMetDependencies(
+                HwvtepOperationalState opState, HwvtepDeviceInfo deviceInfo, T data) {
+
+            Map<Class<? extends Identifiable>, List<InstanceIdentifier>> result = new HashMap<>();
+            Map<Class<? extends Identifiable>, List<InstanceIdentifier<?>>> allKeys = new HashMap<>();
+            allKeys.put(LogicalSwitches.class, getLogicalSwitchDependencies(data));
+            allKeys.put(TerminationPoint.class, getTerminationPointDependencies(data));
+
+            for (Class<? extends Identifiable> cls : allKeys.keySet()) {
+                List<InstanceIdentifier<? extends DataObject>> keysToCheck = allKeys.get(cls);
+                for (InstanceIdentifier<? extends DataObject> key : keysToCheck) {
+                    if (!isDependencyMet(opState, deviceInfo, cls, key)) {
+                        result = addToResultMap(result, cls, key);
+                    }
+                }
+            }
+            return result;
+        }
+
+        Map<Class<? extends Identifiable>, List<InstanceIdentifier>> addToResultMap(
+                Map<Class<? extends Identifiable>, List<InstanceIdentifier>> result,
+                Class<? extends Identifiable> cls, InstanceIdentifier<? extends DataObject> key) {
+            if (null == result) {
+                result = new HashMap<>();
+            }
+            if (!result.containsKey(cls)) {
+                result.put(cls, new ArrayList<>());
+            }
+            result.get(cls).add(key);
+            return result;
+        }
+
+        abstract boolean isDependencyMet(HwvtepOperationalState opState, HwvtepDeviceInfo deviceInfo,
+                                         Class<? extends Identifiable> cls, InstanceIdentifier<? extends DataObject> key);
+    }
+
+    class ConfigDependencyGetter extends DependencyGetter {
+        boolean isDependencyMet(HwvtepOperationalState opState, HwvtepDeviceInfo deviceInfo,
+                                Class<? extends Identifiable> cls, InstanceIdentifier<? extends DataObject> key) {
+            return deviceInfo.isConfigDataAvailable(cls, key) || isConfigDataAvailable(opState, key);
+        }
+
+        boolean isConfigDataAvailable(HwvtepOperationalState opState, InstanceIdentifier<? extends DataObject> key) {
+            DataBroker db = opState.getConnectionInstance().getDataBroker();
+            MdsalUtils mdsalUtils = new MdsalUtils(db);
+            return mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, key) != null;
+        }
+    }
+
+    class InTransitDependencyGetter extends DependencyGetter {
+        boolean isDependencyMet(HwvtepOperationalState opState, HwvtepDeviceInfo deviceInfo,
+                                Class<? extends Identifiable> cls, InstanceIdentifier<? extends DataObject> key) {
+            return opState.isKeyPartOfCurrentTx(cls, key) || !deviceInfo.isKeyInTransit(cls, key);
+        }
+    }
+
+    public abstract List<InstanceIdentifier<?>> getLogicalSwitchDependencies(T data);
+
+    public abstract List<InstanceIdentifier<?>> getTerminationPointDependencies(T data);
+}
\ No newline at end of file
index 2089a1af4ddca54ab4326d24dcf8837f64e6f472..4eba084d7caef9a9a15bc6b410e496e578b0037a 100644 (file)
@@ -19,9 +19,11 @@ import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
 public class HwvtepOperationalCommandAggregator implements TransactionCommand {
 
     private List<TransactionCommand> commands = new ArrayList<>();
+    private final HwvtepConnectionInstance connectionInstance;
 
     public HwvtepOperationalCommandAggregator(HwvtepConnectionInstance key,TableUpdates updates,
             DatabaseSchema dbSchema) {
+        this.connectionInstance = key;
         commands.add(new GlobalUpdateCommand(key, updates, dbSchema));
         commands.add(new HwvtepPhysicalSwitchUpdateCommand(key, updates, dbSchema));
         commands.add(new HwvtepPhysicalSwitchRemoveCommand(key, updates, dbSchema));
@@ -47,5 +49,6 @@ public class HwvtepOperationalCommandAggregator implements TransactionCommand {
         for (TransactionCommand command: commands) {
             command.execute(transaction);
         }
+        connectionInstance.getDeviceInfo().onOpDataAvailable();
     }
 }
index 576f7002392aa742ea1cf8ef546e0e2a75c4322c..5996bcb244ed954718ab3c5a0cefd9aebaf75ee2 100644 (file)
@@ -49,6 +49,7 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.powermock.api.mockito.PowerMockito;
+import org.powermock.api.support.membermodification.MemberMatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -148,6 +149,7 @@ public class DataChangeListenerTestBase extends AbstractDataBrokerTest {
         connectionInfo = mock(OvsdbConnectionInfo.class);
         ovsdbClient = mock(OvsdbClient.class);
         transactionInvoker =  new TransactionInvokerImpl(dataBroker);
+
         connectionInstance = PowerMockito.mock(HwvtepConnectionInstance.class, Mockito.CALLS_REAL_METHODS);
         field(HwvtepConnectionInstance.class, "instanceIdentifier").set(connectionInstance, nodeIid);
         field(HwvtepConnectionInstance.class, "txInvoker").set(connectionInstance, transactionInvoker);
diff --git a/hwvtepsouthbound/hwvtepsouthbound-impl/src/test/java/org/opendaylight/ovsdb/hwvtepsouthbound/DependencyQueueTest.java b/hwvtepsouthbound/hwvtepsouthbound-impl/src/test/java/org/opendaylight/ovsdb/hwvtepsouthbound/DependencyQueueTest.java
new file mode 100644 (file)
index 0000000..df1e7f9
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.DependencyQueue;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.DependentJob;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.HwvtepOperationalState;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.McastMacsRemoteUpdateCommand;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.UnMetDependencyGetter;
+import org.opendaylight.ovsdb.lib.notation.UUID;
+import org.opendaylight.ovsdb.lib.operations.Operations;
+import org.opendaylight.ovsdb.lib.operations.TransactionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
+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.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitchesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacsKey;
+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.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.reflect.Whitebox;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({HwvtepConnectionInstance.class, HwvtepConnectionManager.class, Operations.class, DependencyQueue.class})
+public class DependencyQueueTest extends DataChangeListenerTestBase {
+
+    String[][] terminationPoints = new String[][]{
+            {"192.168.122.20"},
+            {"192.168.122.30"}
+    };
+
+    UnMetDependencyGetter<RemoteMcastMacs> MCAST_MAC_DATA_VALIDATOR;
+    HwvtepOperationalState opState;
+    RemoteMcastMacs mac;
+    InstanceIdentifier<RemoteMcastMacs> macIid;
+    InstanceIdentifier<LogicalSwitches> lsIid;
+    Map<Class<? extends Identifiable>, List<InstanceIdentifier>> unMetDependencies;
+
+    void setupForTest() {
+        MCAST_MAC_DATA_VALIDATOR = Whitebox.getInternalState(McastMacsRemoteUpdateCommand.class, UnMetDependencyGetter.class);
+        opState = new HwvtepOperationalState(connectionInstance);
+        mac = TestBuilders.buildRemoteMcastMacs(nodeIid,"FF:FF:FF:FF:FF:FF", "ls0",
+                new String[]{"192.168.122.20", "192.168.122.30"});
+        lsIid = nodeIid.augmentation(HwvtepGlobalAugmentation.class).
+                child(LogicalSwitches.class, new LogicalSwitchesKey(new HwvtepNodeName("ls0")));
+        macIid = nodeIid.augmentation(HwvtepGlobalAugmentation.class).
+                child(RemoteMcastMacs.class, new RemoteMcastMacsKey(mac.getKey()));
+        Whitebox.setInternalState(DependencyQueue.class, "executorService", MoreExecutors.sameThreadExecutor());
+    }
+
+    @Test
+    public void testLogicalSwitchConfigDependency() throws Exception {
+        setupForTest();
+        unMetDependencies = MCAST_MAC_DATA_VALIDATOR.getUnMetConfigDependencies(opState, mac);
+        unMetDependencies.remove(TerminationPoint.class);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        opState.getDeviceInfo().addJobToQueue(new DependentJob.ConfigWaitingJob(macIid, mac, unMetDependencies) {
+            @Override
+            protected void onDependencyResolved(HwvtepOperationalState operationalState, TransactionBuilder transactionBuilder) {
+                latch.countDown();
+            }
+        });
+        assertEquals(1, latch.getCount());
+        addData(CONFIGURATION, LogicalSwitches.class, new String[]{"ls0", "100"});
+        addData(CONFIGURATION, TerminationPoint.class, terminationPoints);
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    public void testLogicalSwitchInTransitDependency() throws Exception {
+        setupForTest();
+        opState.getDeviceInfo().markKeyAsInTransit(LogicalSwitches.class, lsIid);
+        unMetDependencies = MCAST_MAC_DATA_VALIDATOR.getInTransitDependencies(opState, mac);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        opState.getDeviceInfo().addJobToQueue(new DependentJob.OpWaitingJob<RemoteMcastMacs>(macIid, mac, unMetDependencies) {
+            @Override
+            protected void onDependencyResolved(HwvtepOperationalState operationalState, TransactionBuilder transactionBuilder) {
+                latch.countDown();
+            }
+        });
+        opState.getDeviceInfo().onOpDataAvailable();
+        assertEquals(1, latch.getCount());
+
+        opState.getDeviceInfo().updateDeviceOpData(LogicalSwitches.class, lsIid, new UUID("ls0"), "ls0");
+        opState.getDeviceInfo().onOpDataAvailable();
+        assertEquals(0, latch.getCount());
+
+    }
+}
index 77ad5c4d2bdc316d1998423511b74e3d42a51995..45c010176811131d8098fd0fe1d492a4f86d61cf 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hw
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitchesKey;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
@@ -52,6 +53,10 @@ public class HwvtepOperationalStateTest extends DataChangeListenerTestBase {
         assertEquals(uuid, resultUuid);
 
         boolean result = opState.getDeviceInfo().isKeyInTransit(LogicalSwitches.class, lsIid);
+        assertFalse(result);
+
+        opState.getDeviceInfo().markKeyAsInTransit(LogicalSwitches.class, lsIid);
+        result = opState.getDeviceInfo().isKeyInTransit(LogicalSwitches.class, lsIid);
         assertTrue(result);
 
         opState.getDeviceInfo().updateDeviceOpData(LogicalSwitches.class, lsIid, uuid, lsIid);
diff --git a/hwvtepsouthbound/hwvtepsouthbound-impl/src/test/java/org/opendaylight/ovsdb/hwvtepsouthbound/UnMetDependencyGetterTest.java b/hwvtepsouthbound/hwvtepsouthbound-impl/src/test/java/org/opendaylight/ovsdb/hwvtepsouthbound/UnMetDependencyGetterTest.java
new file mode 100644 (file)
index 0000000..50bf720
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.HwvtepOperationalState;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.McastMacsRemoteUpdateCommand;
+import org.opendaylight.ovsdb.hwvtepsouthbound.transact.UnMetDependencyGetter;
+import org.opendaylight.ovsdb.lib.notation.UUID;
+import org.opendaylight.ovsdb.lib.operations.Operations;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepNodeName;
+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.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitchesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
+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.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.reflect.Whitebox;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({HwvtepConnectionInstance.class, HwvtepConnectionManager.class, Operations.class})
+public class UnMetDependencyGetterTest extends DataChangeListenerTestBase {
+
+    UnMetDependencyGetter<RemoteMcastMacs> MCAST_MAC_DATA_VALIDATOR;
+    HwvtepOperationalState opState;
+    RemoteMcastMacs mac;
+    InstanceIdentifier<LogicalSwitches> lsIid;
+    Map<Class<? extends Identifiable>, List<InstanceIdentifier>> unMetDependencies;
+
+    void setupForTest() {
+        MCAST_MAC_DATA_VALIDATOR = Whitebox.getInternalState(McastMacsRemoteUpdateCommand.class, UnMetDependencyGetter.class);
+        opState = new HwvtepOperationalState(connectionInstance);
+        mac = TestBuilders.buildRemoteMcastMacs(nodeIid,"FF:FF:FF:FF:FF:FF", "ls0",
+                new String[]{"192.168.122.20", "192.168.122.30"});
+        lsIid = nodeIid.augmentation(HwvtepGlobalAugmentation.class).
+                child(LogicalSwitches.class, new LogicalSwitchesKey(new HwvtepNodeName("ls0")));
+    }
+
+    @Test
+    public void testLogicalSwitchConfigDependency() throws Exception {
+        setupForTest();
+        unMetDependencies = MCAST_MAC_DATA_VALIDATOR.getInTransitDependencies(opState, mac);
+        assertEquals(0, unMetDependencies.size());
+
+        unMetDependencies = MCAST_MAC_DATA_VALIDATOR.getUnMetConfigDependencies(opState, mac);
+        assertEquals(1, unMetDependencies.get(LogicalSwitches.class).size());
+        assertEquals(2, unMetDependencies.get(TerminationPoint.class).size());
+
+        opState.getDeviceInfo().updateConfigData(LogicalSwitches.class, lsIid, "ls0");
+        unMetDependencies = MCAST_MAC_DATA_VALIDATOR.getUnMetConfigDependencies(opState, mac);
+        assertNull(unMetDependencies.get(LogicalSwitches.class));
+    }
+
+    @Test
+    public void testLogicalSwitchInTransitDependency() throws Exception {
+        setupForTest();
+        unMetDependencies = MCAST_MAC_DATA_VALIDATOR.getInTransitDependencies(opState, mac);
+        assertEquals(0, unMetDependencies.size());
+
+        opState.getDeviceInfo().markKeyAsInTransit(LogicalSwitches.class, lsIid);
+        unMetDependencies = MCAST_MAC_DATA_VALIDATOR.getInTransitDependencies(opState, mac);
+        assertEquals(1, unMetDependencies.size());
+
+        opState.getDeviceInfo().updateDeviceOpData(LogicalSwitches.class, lsIid, new UUID("ls0"), "ls0");
+        unMetDependencies = MCAST_MAC_DATA_VALIDATOR.getInTransitDependencies(opState, mac);
+        assertEquals(0, unMetDependencies.size());
+    }
+}