Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
index ecf217e..f217c25 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
@@ -1,6 +1,7 @@
package net.floodlightcontroller.core;
import java.util.List;
+import java.util.Map;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
@@ -47,7 +48,7 @@
/**
* Fetch the Switch and Ports info from the Titan Graph
- * and store it locally for fast access during the shortest path
+ * and return it for fast access during the shortest path
* computation.
*
* After fetching the state, method @ref getTopoShortestPath()
@@ -63,14 +64,17 @@
* method @ref dropShortestPathTopo() should be used to release
* the internal state that is not needed anymore:
*
- * prepareShortestPathTopo();
+ * Map<Long, ?> shortestPathTopo;
+ * shortestPathTopo = prepareShortestPathTopo();
* for (int i = 0; i < 10000; i++) {
- * dataPath = getTopoShortestPath(...);
+ * dataPath = getTopoShortestPath(shortestPathTopo, ...);
* ...
* }
- * dropShortestPathTopo();
+ * dropShortestPathTopo(shortestPathTopo);
+ *
+ * @return the Shortest Path info handler stored in a map.
*/
- void prepareShortestPathTopo();
+ Map<Long, ?> prepareShortestPathTopo();
/**
* Release the state that was populated by
@@ -78,8 +82,10 @@
*
* See the documentation for method @ref prepareShortestPathTopo()
* for additional information and usage.
+ *
+ * @shortestPathTopo the Shortest Path info handler to release.
*/
- void dropShortestPathTopo();
+ void dropShortestPathTopo(Map<Long, ?> shortestPathTopo);
/**
* Get the shortest path from a source to a destination by
@@ -89,12 +95,15 @@
* See the documentation for method @ref prepareShortestPathTopo()
* for additional information and usage.
*
+ * @paran shortestPathTopoHandler the Shortest Path info handler
+ * to use.
* @param src the source in the shortest path computation.
* @param dest the destination in the shortest path computation.
* @return the data path with the computed shortest path if
* found, otherwise null.
*/
- DataPath getTopoShortestPath(SwitchPort src, SwitchPort dest);
+ DataPath getTopoShortestPath(Map<Long, ?> shortestPathTopo,
+ SwitchPort src, SwitchPort dest);
/**
* Test whether a route exists from a source to a destination.
diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
index 1bc258b..f8dd52c 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
@@ -17,6 +17,8 @@
package net.floodlightcontroller.core;
+import org.openflow.protocol.OFPhysicalPort;
+
/**
*
*
@@ -44,6 +46,16 @@
public void switchPortChanged(Long switchId);
/**
+ * Fired when ports on a switch area added
+ */
+ public void switchPortAdded(Long switchId, OFPhysicalPort port);
+
+ /**
+ * Fired when ports on a switch area removed
+ */
+ public void switchPortRemoved(Long switchId, OFPhysicalPort port);
+
+ /**
* The name assigned to this listener
* @return
*/
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 0d49c03..842ef35 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -147,7 +147,6 @@
public class Controller implements IFloodlightProviderService,
IStorageSourceListener {
- protected SwitchStorageImpl swStore;;
protected static Logger log = LoggerFactory.getLogger(Controller.class);
@@ -265,18 +264,26 @@
public enum SwitchUpdateType {
ADDED,
REMOVED,
- PORTCHANGED
+ PORTCHANGED,
+ PORTADDED,
+ PORTREMOVED
}
/**
* Update message indicating a switch was added or removed
*/
protected class SwitchUpdate implements IUpdate {
public IOFSwitch sw;
+ public OFPhysicalPort port;
public SwitchUpdateType switchUpdateType;
public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
this.sw = sw;
this.switchUpdateType = switchUpdateType;
}
+ public SwitchUpdate(IOFSwitch sw, OFPhysicalPort port, SwitchUpdateType switchUpdateType) {
+ this.sw = sw;
+ this.port = port;
+ this.switchUpdateType = switchUpdateType;
+ }
public void dispatch() {
if (log.isTraceEnabled()) {
log.trace("Dispatching switch update {} {}",
@@ -294,6 +301,14 @@
case PORTCHANGED:
listener.switchPortChanged(sw.getId());
break;
+ case PORTADDED:
+ listener.switchPortAdded(sw.getId(), port);
+ break;
+ case PORTREMOVED:
+ listener.switchPortRemoved(sw.getId(), port);
+ break;
+ default:
+ break;
}
}
}
@@ -1269,23 +1284,43 @@
((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
sw.setPort(port);
if (!portDown) {
- swStore.addPort(sw.getStringId(), port);
+ SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
+ try {
+ this.updates.put(update);
+ } catch (InterruptedException e) {
+ log.error("Failure adding update to queue", e);
+ }
} else {
- swStore.deletePort(sw.getStringId(), port.getPortNumber());
+ SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
+ try {
+ this.updates.put(update);
+ } catch (InterruptedException e) {
+ log.error("Failure adding update to queue", e);
+ }
}
if (updateStorage)
updatePortInfo(sw, port);
log.debug("Port #{} modified for {}", portNumber, sw);
} else if (m.getReason() == (byte)OFPortReason.OFPPR_ADD.ordinal()) {
sw.setPort(port);
- swStore.addPort(sw.getStringId(), port);
+ SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
+ try {
+ this.updates.put(update);
+ } catch (InterruptedException e) {
+ log.error("Failure adding update to queue", e);
+ }
if (updateStorage)
updatePortInfo(sw, port);
log.debug("Port #{} added for {}", portNumber, sw);
} else if (m.getReason() ==
(byte)OFPortReason.OFPPR_DELETE.ordinal()) {
sw.deletePort(portNumber);
- swStore.deletePort(sw.getStringId(), portNumber);
+ SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
+ try {
+ this.updates.put(update);
+ } catch (InterruptedException e) {
+ log.error("Failure adding update to queue", e);
+ }
if (updateStorage)
removePortInfo(sw, portNumber);
log.debug("Port #{} deleted for {}", portNumber, sw);
@@ -1559,12 +1594,6 @@
}
updateActiveSwitchInfo(sw);
- if (registryService.hasControl(sw.getId())) {
- swStore.update(sw.getStringId(), SwitchState.ACTIVE, DM_OPERATION.UPDATE);
- for (OFPhysicalPort port: sw.getPorts()) {
- swStore.addPort(sw.getStringId(), port);
- }
- }
SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
try {
this.updates.put(update);
@@ -1584,14 +1613,6 @@
// this method is only called after netty has processed all
// pending messages
log.debug("removeSwitch: {}", sw);
- //
- // Cannot set sw to inactive in network map due to race condition
- // Need a cleanup thread to periodically check switches not active in registry
- // and acquire control to set to inactive state in network map and release it
- //
- // if (registryService.hasControl(sw.getId())) {
- // swStore.update(sw.getStringId(), SwitchState.INACTIVE, DM_OPERATION.UPDATE);
- // }
if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
log.debug("Not removing switch {}; already removed", sw);
return;
@@ -2220,8 +2241,6 @@
log.debug("did not get DB config setting using default {}", conf);
}
log.debug("setting DB config {}", conf);
- this.swStore = new SwitchStorageImpl();
- this.swStore.init(conf);
initVendorMessages();
this.systemStartTime = System.currentTimeMillis();
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 57c136c..8aec20f 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -98,6 +98,8 @@
private LinkedList<FlowPath> measurementStoredPaths = new LinkedList<FlowPath>();
private long measurementStartTimeProcessingPaths = 0;
private long measurementEndTimeProcessingPaths = 0;
+ Map<Long, ?> measurementShortestPathTopo = null;
+ private String measurementPerFlowStr = new String();
/** The logger. */
private static Logger log = LoggerFactory.getLogger(FlowManager.class);
@@ -277,7 +279,8 @@
// Fetch and recompute the Shortest Path for those
// Flow Paths this controller is responsible for.
//
- topoRouteService.prepareShortestPathTopo();
+ Map<Long, ?> shortestPathTopo =
+ topoRouteService.prepareShortestPathTopo();
Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
for (IFlowPath flowPathObj : allFlowPaths) {
counterAllFlowPaths++;
@@ -352,7 +355,8 @@
// to avoid closing the transaction.
//
DataPath dataPath =
- topoRouteService.getTopoShortestPath(srcSwitchPort,
+ topoRouteService.getTopoShortestPath(shortestPathTopo,
+ srcSwitchPort,
dstSwitchPort);
if (dataPath == null) {
// We need the DataPath to compare the paths
@@ -377,7 +381,7 @@
conn.utils().removeFlowPath(conn, flowPathObj);
}
- topoRouteService.dropShortestPathTopo();
+ topoRouteService.dropShortestPathTopo(shortestPathTopo);
conn.endTx(Transaction.COMMIT);
@@ -812,7 +816,7 @@
thread.start();
}
- // Want for all threads to complete
+ // Wait for all threads to complete
for (Thread thread : threads) {
try {
thread.join();
@@ -1891,13 +1895,14 @@
// Prepare the Shortest Path computation if the first Flow Path
//
if (measurementStoredPaths.isEmpty())
- topoRouteService.prepareShortestPathTopo();
+ measurementShortestPathTopo = topoRouteService.prepareShortestPathTopo();
//
// Compute the Shortest Path
//
DataPath dataPath =
- topoRouteService.getTopoShortestPath(flowPath.dataPath().srcPort(),
+ topoRouteService.getTopoShortestPath(measurementShortestPathTopo,
+ flowPath.dataPath().srcPort(),
flowPath.dataPath().dstPort());
if (dataPath == null) {
// We need the DataPath to populate the Network MAP
@@ -1956,12 +1961,39 @@
*/
@Override
public boolean measurementInstallPaths(Integer numThreads) {
- List<Thread> threads = new LinkedList<Thread>();
-
// Create a copy of the Flow Paths to install
final ConcurrentLinkedQueue<FlowPath> measurementProcessingPaths =
new ConcurrentLinkedQueue<FlowPath>(measurementStoredPaths);
+ /**
+ * A Thread-wrapper class for executing the threads and collecting
+ * the measurement data.
+ */
+ class MyThread extends Thread {
+ public long[] execTime = new long[2000];
+ public int samples = 0;
+ public int threadId = -1;
+ @Override
+ public void run() {
+ while (true) {
+ FlowPath flowPath = measurementProcessingPaths.poll();
+ if (flowPath == null)
+ return;
+ // Install the Flow Path
+ FlowId flowId = new FlowId();
+ String dataPathSummaryStr =
+ flowPath.dataPath().dataPathSummary();
+ long startTime = System.nanoTime();
+ addFlow(flowPath, flowId, dataPathSummaryStr);
+ long endTime = System.nanoTime();
+ execTime[samples] = endTime - startTime;
+ samples++;
+ }
+ }
+ };
+
+ List<MyThread> threads = new LinkedList<MyThread>();
+
log.debug("Measurement Installing {} flows",
measurementProcessingPaths.size());
@@ -1969,20 +2001,8 @@
// Create the threads to install the Flow Paths
//
for (int i = 0; i < numThreads; i++) {
- Thread thread = new Thread(new Runnable() {
- @Override
- public void run() {
- while (true) {
- FlowPath flowPath = measurementProcessingPaths.poll();
- if (flowPath == null)
- return;
- // Install the Flow Path
- FlowId flowId = new FlowId();
- String dataPathSummaryStr =
- flowPath.dataPath().dataPathSummary();
- addFlow(flowPath, flowId, dataPathSummaryStr);
- }
- }}, "Measurement Add Flow Path");
+ MyThread thread = new MyThread();
+ thread.threadId = i;
threads.add(thread);
}
@@ -1995,7 +2015,7 @@
thread.start();
}
- // Want for all threads to complete
+ // Wait for all threads to complete
for (Thread thread : threads) {
try {
thread.join();
@@ -2007,6 +2027,20 @@
// Record the end of processing
measurementEndTimeProcessingPaths = System.nanoTime();
+ //
+ // Prepare the string with measurement data per each Flow Path
+ // installation.
+ // The string is multiple lines: one line per Flow Path installation:
+ // ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+ //
+ measurementPerFlowStr = new String();
+ String eol = System.getProperty("line.separator");
+ for (MyThread thread : threads) {
+ for (int i = 0; i < thread.samples; i++) {
+ measurementPerFlowStr += "ThreadAndTimePerFlow " + thread.threadId + " " + numThreads + " " + thread.execTime[i] + eol;
+ }
+ }
+
return true;
}
@@ -2023,6 +2057,17 @@
}
/**
+ * Get the measurement install time per Flow.
+ *
+ * @return a multi-line string with the following format per line:
+ * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+ */
+ @Override
+ public String measurementGetPerFlowInstallTime() {
+ return new String(measurementPerFlowStr);
+ }
+
+ /**
* Clear the path flows stored for measurement purpose.
*
* @return true on success, otherwise false.
@@ -2030,9 +2075,10 @@
@Override
public boolean measurementClearAllPaths() {
measurementStoredPaths.clear();
- topoRouteService.dropShortestPathTopo();
+ topoRouteService.dropShortestPathTopo(measurementShortestPathTopo);
measurementStartTimeProcessingPaths = 0;
measurementEndTimeProcessingPaths = 0;
+ measurementPerFlowStr = new String();
return true;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 5e0db35..6c19fd0 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -145,6 +145,14 @@
public Long measurementGetInstallPathsTimeNsec();
/**
+ * Get the measurement install time per Flow.
+ *
+ * @return a multi-line string with the following format per line:
+ * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+ */
+ public String measurementGetPerFlowInstallTime();
+
+ /**
* Clear the path flows stored for measurement purpose.
*
* @return true on success, otherwise false.
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index 9fa5e63..fd9a319 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -25,6 +25,7 @@
router.attach("/measurement-store-path/json", MeasurementStorePathFlowResource.class);
router.attach("/measurement-install-paths/{num-threads}/json", MeasurementInstallPathsFlowResource.class);
router.attach("/measurement-get-install-paths-time-nsec/json", MeasurementGetInstallPathsTimeNsecFlowResource.class);
+ router.attach("/measurement-get-per-flow-install-time/json", MeasurementGetPerFlowInstallTimeFlowResource.class);
router.attach("/measurement-clear-all-paths/json", MeasurementClearAllPathsFlowResource.class);
return router;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java
new file mode 100644
index 0000000..adaecc8
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java
@@ -0,0 +1,37 @@
+package net.floodlightcontroller.flowcache.web;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MeasurementGetPerFlowInstallTimeFlowResource extends ServerResource {
+ protected static Logger log = LoggerFactory.getLogger(MeasurementGetPerFlowInstallTimeFlowResource.class);
+
+ @Get("json")
+ public String retrieve() {
+ String result = null;
+
+ IFlowService flowService =
+ (IFlowService)getContext().getAttributes().
+ get(IFlowService.class.getCanonicalName());
+
+ if (flowService == null) {
+ log.debug("ONOS Flow Service not found");
+ return result;
+ }
+
+ // Extract the arguments
+
+ // Process the request
+ result = flowService.measurementGetPerFlowInstallTime();
+
+ log.debug("Measurement Get Install Paths Time (nsec): " + result);
+
+ return result;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index 634f7eb..72b2988 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -2175,4 +2175,16 @@
public void setAutoPortFastFeature(boolean autoPortFastFeature) {
this.autoPortFastFeature = autoPortFastFeature;
}
+
+ @Override
+ public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+
+ }
}
diff --git a/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java b/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
index 723fe1c..d35a4f8 100644
--- a/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
+++ b/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
@@ -6,12 +6,14 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import org.openflow.protocol.OFPhysicalPort;
import org.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.INetMapStorage.DM_OPERATION;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
import net.floodlightcontroller.core.ISwitchStorage.SwitchState;
import net.floodlightcontroller.core.IOFSwitch;
@@ -50,6 +52,7 @@
protected static final String DBConfigFile = "dbconf";
protected static final String CleanupEnabled = "EnableCleanup";
protected IThreadPoolService threadPool;
+ protected IFloodlightProviderService floodlightProvider;
protected final int CLEANUP_TASK_INTERVAL = 60; // 1 min
protected SingletonTask cleanupTask;
@@ -116,12 +119,29 @@
@Override
public void linkDiscoveryUpdate(LDUpdate update) {
// TODO Auto-generated method stub
+ switch (update.getOperation()) {
+
+ case LINK_REMOVED:
+ // TODO: Move network map link removal here
+ // reconcile paths here
+// IPortObject srcPort = conn.utils().searchPort(conn, HexString.toHexString(update.getSrc()), update.getSrcPort());
+ break;
+
+ default:
+ break;
+ }
}
@Override
public void addedSwitch(IOFSwitch sw) {
- // TODO Auto-generated method stub
+
+ if (registryService.hasControl(sw.getId())) {
+ swStore.update(sw.getStringId(), SwitchState.ACTIVE, DM_OPERATION.UPDATE);
+ for (OFPhysicalPort port: sw.getPorts()) {
+ swStore.addPort(sw.getStringId(), port);
+ }
+ }
}
@@ -137,6 +157,19 @@
}
+
+ @Override
+ public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+ swStore.addPort(HexString.toHexString(switchId), port);
+ }
+
+ @Override
+ public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+ swStore.deletePort(HexString.toHexString(switchId), port.getPortNumber());
+ }
+
@Override
public String getName() {
return "OnosPublisher";
@@ -206,6 +239,8 @@
conn = GraphDBConnection.getInstance(conf);
log = LoggerFactory.getLogger(OnosPublisher.class);
+ floodlightProvider =
+ context.getServiceImpl(IFloodlightProviderService.class);
deviceService = context.getServiceImpl(IDeviceService.class);
threadPool = context.getServiceImpl(IThreadPoolService.class);
registryService = context.getServiceImpl(IControllerRegistryService.class);
@@ -227,6 +262,7 @@
String cleanupNeeded = configMap.get(CleanupEnabled);
deviceService.addListener(this);
+ floodlightProvider.addOFSwitchListener(this);
log.debug("Adding EventListener");
conn.addEventListener(new LocalTopologyEventListener(conn));
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
index 95a3b19..1e002aa 100644
--- a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -101,14 +101,6 @@
GraphDBConnection conn;
- //
- // Topology state for storing (on demand) Switch and Ports info for
- // fast access during the shortest path computation.
- // It is explicitly populated by method @ref prepareShortestPathTopo().
- // See the documentation for that method for details.
- //
- HashMap<Long, Node> shortestPathTopo;
-
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l =
@@ -168,7 +160,7 @@
/**
* Fetch the Switch and Ports info from the Titan Graph
- * and store it locally for fast access during the shortest path
+ * and return it for fast access during the shortest path
* computation.
*
* After fetching the state, method @ref getTopoShortestPath()
@@ -184,16 +176,18 @@
* method @ref dropShortestPathTopo() should be used to release
* the internal state that is not needed anymore:
*
- * prepareShortestPathTopo();
+ * Map<Long, ?> shortestPathTopo;
+ * shortestPathTopo = prepareShortestPathTopo();
* for (int i = 0; i < 10000; i++) {
- * dataPath = getTopoShortestPath(...);
+ * dataPath = getTopoShortestPath(shortestPathTopo, ...);
* ...
* }
- * dropShortestPathTopo();
+ * dropShortestPathTopo(shortestPathTopo);
+ *
+ * @return the Shortest Path info handler stored in a map.
*/
-
- public void prepareShortestPathTopo() {
- shortestPathTopo = new HashMap<Long, Node>();
+ public Map<Long, ?> prepareShortestPathTopo() {
+ Map<Long, Node> shortestPathTopo = new HashMap<Long, Node>();
//
// Fetch the relevant info from the Switch and Port vertices
@@ -260,6 +254,8 @@
}
}
conn.endTx(Transaction.COMMIT);
+
+ return shortestPathTopo;
}
/**
@@ -268,9 +264,10 @@
*
* See the documentation for method @ref prepareShortestPathTopo()
* for additional information and usage.
+ *
+ * @shortestPathTopo the Shortest Path info handler to release.
*/
-
- public void dropShortestPathTopo() {
+ public void dropShortestPathTopo(Map<Long, ?> shortestPathTopo) {
shortestPathTopo = null;
}
@@ -282,13 +279,17 @@
* See the documentation for method @ref prepareShortestPathTopo()
* for additional information and usage.
*
+ * @param shortestPathTopoHandler the Shortest Path info handler
+ * to use.
* @param src the source in the shortest path computation.
* @param dest the destination in the shortest path computation.
* @return the data path with the computed shortest path if
* found, otherwise null.
*/
-
- public DataPath getTopoShortestPath(SwitchPort src, SwitchPort dest) {
+ public DataPath getTopoShortestPath(Map<Long, ?> shortestPathTopoHandler,
+ SwitchPort src, SwitchPort dest) {
+ @SuppressWarnings("unchecked")
+ Map<Long, Node> shortestPathTopo = (Map)shortestPathTopoHandler;
DataPath result_data_path = new DataPath();
// Initialize the source and destination in the data path to return
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
index 4ed59d7..371a479 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
@@ -39,6 +39,7 @@
import org.openflow.protocol.OFFlowRemoved;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPhysicalPort;
import org.openflow.protocol.OFType;
import org.openflow.protocol.factory.BasicFactory;
import org.openflow.util.HexString;
@@ -675,5 +676,17 @@
Map<String, String> removedControllerNodeIPs) {
// ignore
}
+
+ @Override
+ public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+
+ }
}
diff --git a/web/measurement_get_per_flow_install_time.py b/web/measurement_get_per_flow_install_time.py
new file mode 100755
index 0000000..bf2bcc7
--- /dev/null
+++ b/web/measurement_get_per_flow_install_time.py
@@ -0,0 +1,61 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import pprint
+import os
+import sys
+import subprocess
+import json
+import argparse
+import io
+import time
+
+from flask import Flask, json, Response, render_template, make_response, request
+
+#
+# TODO: remove this! We don't use JSON argument here!
+# curl http://127.0.0.1:8080/wm/flow/delete/{"value":"0xf"}/json'
+#
+
+## Global Var ##
+ControllerIP="127.0.0.1"
+ControllerPort=8080
+
+DEBUG=0
+pp = pprint.PrettyPrinter(indent=4)
+
+app = Flask(__name__)
+
+## Worker Functions ##
+def log_error(txt):
+ print '%s' % (txt)
+
+def debug(txt):
+ if DEBUG:
+ print '%s' % (txt)
+
+# @app.route("/wm/flow/measurement-get-per-flow-install-time/json")
+def measurement_get_per_flow_install_time():
+ command = "curl -s \"http://%s:%s/wm/flow/measurement-get-per-flow-install-time/json\"" % (ControllerIP, ControllerPort)
+ debug("measurement_get_per_flow_install_time %s" % command)
+ result = os.popen(command).read()
+ print '%s' % (result)
+ # parsedResult = json.loads(result)
+ # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+ usage_msg = "Get the measured time per flow to install each stored flow path\n"
+ usage_msg = usage_msg + "Usage: %s\n" % (sys.argv[0])
+ usage_msg = usage_msg + "\n"
+
+ # app.debug = False;
+
+ # Usage info
+ if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+ print(usage_msg)
+ exit(0)
+
+ # Check arguments
+
+ # Do the work
+ measurement_get_per_flow_install_time()
diff --git a/web/measurement_process.py b/web/measurement_process.py
new file mode 100755
index 0000000..3187299
--- /dev/null
+++ b/web/measurement_process.py
@@ -0,0 +1,59 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import functools
+import math
+import sys
+
+## {{{ http://code.activestate.com/recipes/511478/ (r1)
+
+def percentile(N, percent, key=lambda x:x):
+ """
+ Find the percentile of a list of values.
+
+ @parameter N - is a list of values. Note N MUST BE already sorted.
+ @parameter percent - a float value from 0.0 to 1.0.
+ @parameter key - optional key function to compute value from each element of N.
+
+ @return - the percentile of the values
+ """
+ if not N:
+ return None
+ k = (len(N)-1) * percent
+ f = math.floor(k)
+ c = math.ceil(k)
+ if f == c:
+ return key(N[int(k)])
+ d0 = key(N[int(f)]) * (c-k)
+ d1 = key(N[int(c)]) * (k-f)
+ return d0+d1
+
+# median is 50th percentile.
+# median = functools.partial(percentile, percent=0.5)
+## end of http://code.activestate.com/recipes/511478/ }}}
+
+if __name__ == "__main__":
+
+ dict = {}
+
+ #
+ # Read the data from the stdin, and store it in a dictionary.
+ # The dictionary uses lists as values.
+ #
+ data = sys.stdin.readlines()
+ for line in data:
+ words = line.split()
+ thread_n = int(words[0])
+ msec = float(words[1])
+ dict.setdefault(thread_n, []).append(msec)
+
+ #
+ # Compute and print the values: median (50-th), 10-th, and 90-th
+ # percentile:
+ # <key> <median> <10-percentile> <90-percentile>
+ #
+ for key, val_list in sorted(dict.items()):
+ val_10 = percentile(sorted(val_list), 0.1)
+ val_50 = percentile(sorted(val_list), 0.5)
+ val_90 = percentile(sorted(val_list), 0.9)
+ print "%s %s %s %s" % (str(key), str(val_50), str(val_10), str(val_90))
diff --git a/web/measurement_run.py b/web/measurement_run.py
new file mode 100755
index 0000000..80d0517
--- /dev/null
+++ b/web/measurement_run.py
@@ -0,0 +1,104 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import os
+import string
+import subprocess
+import time
+
+# flow_n = 252
+# threads_n = [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100]
+# iterations_n = 10
+
+flow_n = 1
+threads_n = [1]
+iterations_n = 10
+# iterations_n = 100
+
+# flow_n = 42
+# flow_n = 420
+# flow_n = 1008
+
+def run_command(cmd):
+ """
+ - Run an external command, and return a tuple: stdout as the
+ first argument, and stderr as the second argument.
+ - Returns None if error.
+ """
+ try:
+ pr = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+ ret_tuple = pr.communicate();
+ if pr.returncode:
+ print "%s failed with error code: %s" % (cmd, str(pr.returncode))
+ return ret_tuple
+ except OSError:
+ print "OS Error running %s" % cmd
+
+def run_install_paths(flowdef_filename):
+ # Prepare the flows to measure
+ cmd = "web/measurement_store_flow.py -f " + flowdef_filename
+ os.system(cmd)
+
+def run_measurement(thread_n):
+ # Install the Flow Paths
+ cmd = ["web/measurement_install_paths.py", str(thread_n)]
+ run_command(cmd)
+
+ # Get the measurement data and print it
+ cmd = "web/measurement_get_install_paths_time_nsec.py"
+ r = run_command(cmd) # Tuple: [<stdout>, <stderr>]
+ res = r[0].split() # Tuple: [<num>, nsec]
+ nsec_str = res[0]
+ msec = float(nsec_str) / (1000 * 1000)
+
+ # Get the measurement data and print it
+ cmd = "web/measurement_get_per_flow_install_time.py"
+ r = run_command(cmd) # Tuple: [<stdout>, <stderr>]
+ res = r[0]
+ print res
+
+ # Keep checking until all Flow Paths are installed
+ while True:
+ # time.sleep(3)
+ cmd = ["web/get_flow.py", "all"]
+ r = run_command(cmd)
+ if string.count(r[0], "FlowPath") != flow_n:
+ continue
+ if string.find(r[0], "NOT") == -1:
+ break
+
+ # Remove the installed Flow Paths
+ cmd = ["web/delete_flow.py", "all"]
+ run_command(cmd)
+
+ # Keep checking until all Flows are removed
+ while True:
+ # time.sleep(3)
+ cmd = ["web/get_flow.py", "all"]
+ r = run_command(cmd)
+ if r[0] == "":
+ break
+
+ return msec
+
+
+if __name__ == "__main__":
+
+ # Initial cleanup
+ cmd = "web/measurement_clear_all_paths.py"
+ run_command(cmd)
+
+ # Install the Flow Paths to measure
+ flowdef_filename = "web/flowdef_8node_" + str(flow_n) + ".txt"
+ run_install_paths(flowdef_filename)
+
+ # Do the work
+ for thread_n in threads_n:
+ for n in range(iterations_n):
+ msec = run_measurement(thread_n)
+ # Format: <number of threads> <time in ms>
+ print "%d %f" % (thread_n, msec / flow_n)
+
+ # Cleanup on exit
+ cmd = "web/measurement_clear_all_paths.py"
+ run_command(cmd)