Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index 18da3dd..71b6593 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -191,6 +191,12 @@
@Property("dst_port")
public void setDstPort(Short dstPort);
+ @Property("data_path_summary")
+ public String getDataPathSummary();
+
+ @Property("data_path_summary")
+ public void setDataPathSummary(String dataPathSummary);
+
@Adjacency(label="flow", direction=Direction.IN)
public Iterable<IFlowEntry> getFlowEntries();
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 25cf367..34e6296 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -263,7 +263,6 @@
log.debug("FloodlightProvider service not found!");
return;
}
-
Map<Long, IOFSwitch> mySwitches =
floodlightProvider.getSwitches();
Map<Long, IFlowEntry> myFlowEntries =
@@ -272,6 +271,77 @@
new LinkedList<IFlowEntry>();
//
+ // Fetch and recompute the Shortest Path for those
+ // Flow Paths this controller is responsible for.
+ //
+
+ /*
+ * TODO: For now, the computation of the reconciliation is
+ * commented-out.
+ */
+ /*
+ topoRouteService.prepareShortestPathTopo();
+ Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
+ HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
+ for (IFlowPath flowPathObj : allFlowPaths) {
+ if (flowPathObj == null)
+ continue;
+ String dataPathSummaryStr = flowPathObj.getDataPathSummary();
+ if (dataPathSummaryStr == null)
+ continue; // Could be invalid entry?
+ if (dataPathSummaryStr.isEmpty())
+ continue; // No need to maintain this flow
+
+ // Fetch the fields needed to recompute the shortest path
+ String flowIdStr = flowPathObj.getFlowId();
+ String srcDpidStr = flowPathObj.getSrcSwitch();
+ Short srcPortShort = flowPathObj.getSrcPort();
+ String dstDpidStr = flowPathObj.getDstSwitch();
+ Short dstPortShort = flowPathObj.getDstPort();
+ if ((flowIdStr == null) ||
+ (srcDpidStr == null) ||
+ (srcPortShort == null) ||
+ (dstDpidStr == null) ||
+ (dstPortShort == null)) {
+ log.debug("IGNORING Flow Path entry with null fields");
+ continue;
+ }
+
+ FlowId flowId = new FlowId(flowIdStr);
+ Dpid srcDpid = new Dpid(srcDpidStr);
+ Port srcPort = new Port(srcPortShort);
+ Dpid dstDpid = new Dpid(dstDpidStr);
+ Port dstPort = new Port(dstPortShort);
+ SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
+ SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
+ DataPath dataPath =
+ topoRouteService.getTopoShortestPath(srcSwitchPort,
+ dstSwitchPort);
+ String newDataPathSummaryStr = dataPath.dataPathSummary();
+ if (dataPathSummaryStr.equals(newDataPathSummaryStr))
+ continue; // Nothing changed
+
+ //
+ // Use the source DPID as a heuristic to decide
+ // which controller is responsible for maintaining the
+ // shortest path.
+ // NOTE: This heuristic is error-prone: if the switch
+ // goes away and no controller is responsible for that
+ // switch, then the original Flow Path is not cleaned-up
+ //
+ IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
+ if (mySwitch == null)
+ continue; // Ignore: not my responsibility
+
+ log.debug("RECONCILE: Need to Reconcile Shortest Path for FlowID {}",
+ flowId.toString());
+ flowObjSet.add(flowPathObj);
+ }
+ reconcileFlows(flowObjSet);
+ topoRouteService.dropShortestPathTopo();
+ */
+
+ //
// Fetch all Flow Entries and select only my Flow Entries
// that need to be undated into the switches.
//
@@ -593,10 +663,13 @@
*
* @param flowPath the Flow Path to install.
* @param flowId the return-by-reference Flow ID as assigned internally.
+ * @param dataPathSummaryStr the data path summary string if the added
+ * flow will be maintained internally, otherwise null.
* @return true on success, otherwise false.
*/
@Override
- public boolean addFlow(FlowPath flowPath, FlowId flowId) {
+ public boolean addFlow(FlowPath flowPath, FlowId flowId,
+ String dataPathSummaryStr) {
if (flowPath.flowId().value() == measurementFlowId) {
modifiedMeasurementFlowTime = System.nanoTime();
}
@@ -654,6 +727,12 @@
flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
+ if (dataPathSummaryStr != null) {
+ flowObj.setDataPathSummary(dataPathSummaryStr);
+ } else {
+ flowObj.setDataPathSummary("");
+ }
+
// Flow edges:
// HeadFE
@@ -1041,14 +1120,14 @@
return flowPaths;
}
- Collections.sort(allFlows);
+// Collections.sort(allFlows);
for (FlowPath flow : allFlows) {
// start from desired flowId
- if (flow.flowId().value() < flowId.value()) {
- continue;
- }
+ //if (flow.flowId().value() < flowId.value()) {
+ // continue;
+ //}
// Summarize by making null flow entry fields that are not relevant to report
for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
@@ -1125,16 +1204,16 @@
String flowIdStr = flowObj.getFlowId();
String installerIdStr = flowObj.getInstallerId();
String srcSwitchStr = flowObj.getSrcSwitch();
- Short srcPortStr = flowObj.getSrcPort();
+ Short srcPortShort = flowObj.getSrcPort();
String dstSwitchStr = flowObj.getDstSwitch();
- Short dstPortStr = flowObj.getDstPort();
+ Short dstPortShort = flowObj.getDstPort();
if ((flowIdStr == null) ||
(installerIdStr == null) ||
(srcSwitchStr == null) ||
- (srcPortStr == null) ||
+ (srcPortShort == null) ||
(dstSwitchStr == null) ||
- (dstPortStr == null)) {
+ (dstPortShort == null)) {
// TODO: A work-around, becauuse of some bogus database objects
return null;
}
@@ -1142,9 +1221,9 @@
flowPath.setFlowId(new FlowId(flowIdStr));
flowPath.setInstallerId(new CallerId(installerIdStr));
flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
- flowPath.dataPath().srcPort().setPort(new Port(srcPortStr));
+ flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
- flowPath.dataPath().dstPort().setPort(new Port(dstPortStr));
+ flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
//
// Extract all Flow Entries
@@ -1229,6 +1308,8 @@
*/
@Override
public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
+ String dataPathSummaryStr = null;
+
//
// Do the shortest path computation
//
@@ -1243,6 +1324,9 @@
userFlowEntryMatch = flowPath.dataPath().flowEntries().get(0).flowEntryMatch();
}
+ // Compute the Data Path summary
+ dataPathSummaryStr = dataPath.dataPathSummary();
+
//
// Set the incoming port matching and the outgoing port output
// actions for each flow entry.
@@ -1277,7 +1361,7 @@
computedFlowPath.setDataPath(dataPath);
FlowId flowId = new FlowId();
- if (! addFlow(computedFlowPath, flowId))
+ if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
return null;
// TODO: Mark the flow for maintenance purpose
@@ -1352,21 +1436,37 @@
if (flowObj != null)
flowObjSet.add(flowObj);
}
- // conn.endTx(Transaction.COMMIT);
+
+ // Reconcile the affected flows
+ reconcileFlows(flowObjSet);
+ }
+
+ /**
+ * Reconcile all flows in a set.
+ *
+ * @param flowObjSet the set of flows that need to be reconciliated.
+ */
+ public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
+ if (! flowObjSet.iterator().hasNext())
+ return;
//
// Remove the old Flow Entries, and add the new Flow Entries
//
+
Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+ LinkedList<FlowPath> flowPaths = new LinkedList<FlowPath>();
for (IFlowPath flowObj : flowObjSet) {
FlowPath flowPath = extractFlowPath(flowObj);
if (flowPath == null)
continue;
+ flowPaths.add(flowPath);
//
// Remove my Flow Entries from the Network MAP
//
Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+ LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
for (IFlowEntry flowEntryObj : flowEntries) {
String dpidStr = flowEntryObj.getSwitchDpid();
if (dpidStr == null)
@@ -1375,10 +1475,15 @@
IOFSwitch mySwitch = mySwitches.get(dpid.value());
if (mySwitch == null)
continue; // Ignore the entry: not my switch
+ deleteFlowEntries.add(flowEntryObj);
+ }
+ for (IFlowEntry flowEntryObj : deleteFlowEntries) {
flowObj.removeFlowEntry(flowEntryObj);
conn.utils().removeFlowEntry(conn, flowEntryObj);
}
+ }
+ for (FlowPath flowPath : flowPaths) {
//
// Delete the flow entries from the switches
//
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index b6df1e2..619d36b 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -20,9 +20,12 @@
*
* @param flowPath the Flow Path to install.
* @param flowId the return-by-reference Flow ID as assigned internally.
+ * @param dataPathSummaryStr the data path summary string if the added
+ * flow will be maintained internally, otherwise null.
* @return true on success, otherwise false.
*/
- boolean addFlow(FlowPath flowPath, FlowId flowId);
+ boolean addFlow(FlowPath flowPath, FlowId flowId,
+ String dataPathSummaryStr);
/**
* Delete a previously added flow.
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
index cdccae1..e266e2e 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
@@ -52,7 +52,7 @@
// Process the request
if (flowPath != null) {
- if (flowService.addFlow(flowPath, result) != true) {
+ if (flowService.addFlow(flowPath, result, null) != true) {
result = new FlowId(); // Error: Return empty Flow Id
}
}
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
index fd7c364..ca8d92f 100644
--- a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -13,6 +13,7 @@
import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
+import net.floodlightcontroller.core.ISwitchStorage.SwitchState;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -23,14 +24,12 @@
import net.floodlightcontroller.util.Port;
import net.floodlightcontroller.util.SwitchPort;
import net.onrc.onos.util.GraphDBConnection;
+import net.onrc.onos.util.GraphDBConnection.Transaction;
import org.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.thinkaurelius.titan.core.TitanFactory;
-import com.thinkaurelius.titan.core.TitanGraph;
-import com.thinkaurelius.titan.core.TitanTransaction;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.TransactionalGraph.Conclusion;
import com.tinkerpop.blueprints.Vertex;
@@ -194,17 +193,15 @@
*/
public void prepareShortestPathTopo() {
- TitanGraph titanGraph = TitanFactory.open("/tmp/cassandra.titan");
- TitanTransaction titanTransaction = titanGraph.startTransaction();
shortestPathTopo = new HashMap<Long, Node>();
//
// Fetch the relevant info from the Switch and Port vertices
// from the Titan Graph.
//
- Iterable<Vertex> nodes = titanTransaction.getVertices("type", "switch");
- for (Vertex nodeVertex : nodes) {
-
+ Iterable<ISwitchObject> nodes = conn.utils().getActiveSwitches(conn);
+ for (ISwitchObject switchObj : nodes) {
+ Vertex nodeVertex = switchObj.asVertex();
//
// The Switch info
//
@@ -245,6 +242,11 @@
// The neighbor Switch info
//
for (Vertex neighborVertex : neighborPortVertex.getVertices(Direction.IN, "on")) {
+ // Ignore inactive switches
+ String state = neighborVertex.getProperty("state").toString();
+ if (! state.equals(SwitchState.ACTIVE.toString()))
+ continue;
+
String neighborDpid = neighborVertex.getProperty("dpid").toString();
long neighborId = HexString.toLong(neighborDpid);
Node neighbor = shortestPathTopo.get(neighborId);
@@ -257,8 +259,7 @@
}
}
}
-
- titanTransaction.stopTransaction(Conclusion.SUCCESS);
+ conn.endTx(Transaction.COMMIT);
}
/**
@@ -367,7 +368,7 @@
Port outPort;
for (Node.Link link: resultPath) {
// Setup the outgoing port, and add the Flow Entry
- outPort = new Port(link.neighborPort);
+ outPort = new Port(link.myPort);
FlowEntry flowEntry = new FlowEntry();
flowEntry.setDpid(new Dpid(link.me.nodeId));
@@ -409,12 +410,17 @@
result_data_path.setSrcPort(src);
result_data_path.setDstPort(dest);
- TitanGraph titanGraph = TitanFactory.open("/tmp/cassandra.titan");
- TitanTransaction titanTransaction = titanGraph.startTransaction();
-
String dpid_src = src.dpid().toString();
String dpid_dest = dest.dpid().toString();
+ // Get the source and destination switches
+ ISwitchObject srcSwitch =
+ conn.utils().searchActiveSwitch(conn, dpid_src);
+ ISwitchObject destSwitch =
+ conn.utils().searchActiveSwitch(conn, dpid_dest);
+ if (srcSwitch == null || destSwitch == null) {
+ return null;
+ }
//
// Test whether we are computing a path from/to the same DPID.
@@ -426,22 +432,10 @@
flowEntry.setInPort(src.port());
flowEntry.setOutPort(dest.port());
result_data_path.flowEntries().add(flowEntry);
- titanTransaction.stopTransaction(Conclusion.SUCCESS);
- // titanTransaction.shutdown();
+ conn.endTx(Transaction.COMMIT);
return result_data_path;
}
-
- // Get the source vertex
-
- ISwitchObject srcSwitch = conn.utils().searchSwitch(conn, dpid_src);
- ISwitchObject destSwitch = conn.utils().searchSwitch(conn, dpid_dest);
-
- if (srcSwitch == null || destSwitch == null) {
- return null;
- }
-
-
Vertex v_src = srcSwitch.asVertex();
Vertex v_dest = destSwitch.asVertex();
@@ -464,6 +458,11 @@
for (Vertex parentPort : nextVertex.getVertices(Direction.OUT, "on")) {
for (Vertex childPort : parentPort.getVertices(Direction.OUT, "link")) {
for (Vertex child : childPort.getVertices(Direction.IN, "on")) {
+ // Ignore inactive switches
+ String state = child.getProperty("state").toString();
+ if (! state.equals(SwitchState.ACTIVE.toString()))
+ continue;
+
if (! visitedSet.contains(child)) {
previousVertexMap.put(parentPort, nextVertex);
previousVertexMap.put(childPort, parentPort);
@@ -489,7 +488,6 @@
Collections.reverse(resultPath);
-
//
// Loop through the result and prepare the return result
// as a list of Flow Entries.
@@ -553,8 +551,7 @@
result_data_path.flowEntries().add(flowEntry);
}
- titanTransaction.stopTransaction(Conclusion.SUCCESS);
- // titanTransaction.shutdown();
+ conn.endTx(Transaction.COMMIT);
if (result_data_path.flowEntries().size() > 0)
return result_data_path;
diff --git a/src/main/java/net/floodlightcontroller/util/DataPath.java b/src/main/java/net/floodlightcontroller/util/DataPath.java
index b2dded6..0ca0d13 100644
--- a/src/main/java/net/floodlightcontroller/util/DataPath.java
+++ b/src/main/java/net/floodlightcontroller/util/DataPath.java
@@ -79,6 +79,37 @@
}
/**
+ * Get a string with the summary of the shortest-path data path
+ * computation.
+ *
+ * NOTE: This method assumes the DataPath was created by
+ * using FlowManager::getShortestPath() so the inPort and outPort
+ * of the Flow Entries are set.
+ * NOTE: This method is a temporary solution and will be removed
+ * in the future.
+ *
+ * @return a string with the summary of the shortest-path
+ * data path computation if valid, otherwise the string "X".
+ * If the shortest-path was valid, The string has the following form:
+ * inPort/dpid/outPort;inPort/dpid/outPort;...
+ */
+ public String dataPathSummary() {
+ String resultStr = new String();
+ if (this.flowEntries != null) {
+ for (FlowEntry flowEntry : this.flowEntries) {
+ // The data path summary string
+ resultStr = resultStr +
+ flowEntry.inPort().toString() + "/"
+ + flowEntry.dpid().toString() + "/" +
+ flowEntry.outPort().toString() + ";";
+ }
+ }
+ if (resultStr.isEmpty())
+ resultStr = "X"; // Invalid shortest-path
+ return resultStr;
+ }
+
+ /**
* Convert the data path to a string.
*
* The string has the following form:
diff --git a/src/main/java/net/onrc/onos/util/GraphDBUtils.java b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
index ba48103..7283d09 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
@@ -44,6 +44,16 @@
}
@Override
+ public ISwitchObject searchActiveSwitch(GraphDBConnection conn, String dpid) {
+ ISwitchObject sw = searchSwitch(conn, dpid);
+ if ((sw != null) &&
+ sw.getState().equals(SwitchState.ACTIVE.toString())) {
+ return sw;
+ }
+ return null;
+ }
+
+ @Override
public IDeviceObject searchDevice(GraphDBConnection conn, String macAddr) {
// TODO Auto-generated method stub
FramedGraph<TitanGraph> fg = conn.getFramedGraph();
diff --git a/src/main/java/net/onrc/onos/util/IDBUtils.java b/src/main/java/net/onrc/onos/util/IDBUtils.java
index 864e227..51948a2 100644
--- a/src/main/java/net/onrc/onos/util/IDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/IDBUtils.java
@@ -10,6 +10,7 @@
public interface IDBUtils {
public ISwitchObject searchSwitch(GraphDBConnection conn, String dpid);
+ public ISwitchObject searchActiveSwitch(GraphDBConnection conn, String dpid);
public Iterable<ISwitchObject> getActiveSwitches(GraphDBConnection conn);
public Iterable<ISwitchObject> getAllSwitches(GraphDBConnection conn);
public Iterable<ISwitchObject> getInactiveSwitches(GraphDBConnection conn);
diff --git a/web/add_flow.py b/web/add_flow.py
index 0ab6847..5a248eb 100755
--- a/web/add_flow.py
+++ b/web/add_flow.py
@@ -21,6 +21,7 @@
ControllerIP = "127.0.0.1"
ControllerPort = 8080
MonitoringEnabled = False
+MonitoringByOnos = False
ReadFromFile = ""
DEBUG=0
@@ -91,6 +92,20 @@
log_error("Controller IF has issue")
exit(1)
+def add_shortest_path_flow(flow_path):
+ flow_path_json = json.dumps(flow_path)
+
+ try:
+ command = "curl -s -H 'Content-Type: application/json' -d '%s' http://%s:%s/wm/flow/add-shortest-path/json" % (flow_path_json, ControllerIP, ControllerPort)
+ debug("add_shortest_path_flow %s" % command)
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ # parsedResult = json.loads(result)
+ # debug("parsed %s" % parsedResult)
+ except:
+ log_error("Controller IF has issue")
+ exit(1)
+
def delete_flow_path(flow_id):
command = "curl -s \"http://%s:%s/wm/flow/delete/%s/json\"" % (ControllerIP, ControllerPort, flow_id)
debug("delete_flow_path %s" % command)
@@ -366,17 +381,97 @@
flow_path['dataPath'] = my_data_path
debug("Flow Path: %s" % flow_path)
+ return flow_path
- add_flow_path(flow_path)
+def exec_monitoring_by_onos(parsed_args):
+ idx = 0
+ while idx < len(parsed_args):
+ data_path = {}
+ src_dpid = {}
+ src_port = {}
+ dst_dpid = {}
+ dst_port = {}
+ src_switch_port = {}
+ dst_switch_port = {}
+ flow_entry = {}
+ flow_entries = []
+
+ src_dpid['value'] = parsed_args[idx]['my_src_dpid']
+ src_port['value'] = parsed_args[idx]['my_src_port']
+ dst_dpid['value'] = parsed_args[idx]['my_dst_dpid']
+ dst_port['value'] = parsed_args[idx]['my_dst_port']
+ src_switch_port['dpid'] = src_dpid
+ src_switch_port['port'] = src_port
+ dst_switch_port['dpid'] = dst_dpid
+ dst_switch_port['port'] = dst_port
+ match = parsed_args[idx]['match']
+ flow_entry['flowEntryMatch'] = match
+ flow_entries.append(flow_entry)
+
+ data_path['srcPort'] = copy.deepcopy(src_switch_port)
+ data_path['dstPort'] = copy.deepcopy(dst_switch_port)
+ data_path['flowEntries'] = copy.deepcopy(flow_entries)
+
+ #
+ # XXX: Explicitly disable the InPort matching, and
+ # the Output action, because they get in the way
+ # during the compute_flow_path() processing.
+ #
+ parsed_args[idx]['matchInPortEnabled'] = False
+ parsed_args[idx]['actionOutputEnabled'] = False
+
+ flow_path = compute_flow_path(parsed_args[idx], data_path)
+ add_shortest_path_flow(flow_path)
+
+ idx = idx + 1
+
+
+def exec_processing_by_script(parsed_args):
+ #
+ # Initialization
+ #
+ last_data_paths = []
+ idx = 0
+ while idx < len(parsed_args):
+ last_data_path = []
+ last_data_paths.append(copy.deepcopy(last_data_path))
+ idx = idx + 1
+
+ #
+ # Do the work: install and/or periodically monitor each flow
+ #
+ while True:
+ idx = 0
+ while idx < len(parsed_args):
+ last_data_path = last_data_paths[idx]
+ my_flow_id = parsed_args[idx]['my_flow_id']
+ data_path = compute_data_path(parsed_args[idx])
+ if data_path != last_data_path:
+ print_data_path(data_path)
+ if len(last_data_path) > 0:
+ delete_flow_path(my_flow_id)
+ if len(data_path) > 0:
+ flow_path = compute_flow_path(parsed_args[idx], data_path)
+ add_flow_path(flow_path)
+ last_data_paths[idx] = copy.deepcopy(data_path)
+ idx = idx + 1
+
+ if MonitoringEnabled != True:
+ break
+ time.sleep(1)
if __name__ == "__main__":
usage_msg = "Usage: %s [Flags] <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port> [Match Conditions] [Actions]\n" % (sys.argv[0])
usage_msg = usage_msg + "\n"
usage_msg = usage_msg + " Flags:\n"
- usage_msg = usage_msg + " -m Monitor and maintain the installed shortest path(s)\n"
- usage_msg = usage_msg + " -f <filename> Read the flow(s) to install from a file\n"
- usage_msg = usage_msg + " File format: one line per flow starting with <flow-id>\n"
+ usage_msg = usage_msg + " -m [monitorname] Monitor and maintain the installed shortest path(s)\n"
+ usage_msg = usage_msg + " If 'monitorname' is specified and is set to 'ONOS'\n"
+ usage_msg = usage_msg + " ((case insensitive), then the flow generation and\n"
+ usage_msg = usage_msg + " maintanenance is done by ONOS itself.\n"
+ usage_msg = usage_msg + " Otherwise, it is done by this script.\n"
+ usage_msg = usage_msg + " -f <filename> Read the flow(s) to install from a file\n"
+ usage_msg = usage_msg + " File format: one line per flow starting with <flow-id>\n"
usage_msg = usage_msg + "\n"
usage_msg = usage_msg + " Match Conditions:\n"
usage_msg = usage_msg + " matchInPort <True|False> (default to True)\n"
@@ -427,6 +522,11 @@
idx = idx + 1
if arg1 == "-m":
MonitoringEnabled = True
+ if idx < len(sys.argv):
+ arg2 = sys.argv[idx]
+ if arg2.lower() == "onos":
+ MonitoringByOnos = True
+ idx = idx + 1
start_argv_index = idx
elif arg1 == "-f":
if idx >= len(sys.argv):
@@ -476,24 +576,8 @@
idx = idx + 1
#
- # Do the work: install and/or periodically monitor each flow
- #
- while True:
- idx = 0
- while idx < len(parsed_args):
- last_data_path = last_data_paths[idx]
- my_flow_id = parsed_args[idx]['my_flow_id']
- data_path = compute_data_path(parsed_args[idx])
- if data_path != last_data_path:
- print_data_path(data_path)
- if len(last_data_path) > 0:
- delete_flow_path(my_flow_id)
- if len(data_path) > 0:
- flow_path = compute_flow_path(parsed_args[idx], data_path)
- add_flow_path(flow_path)
- last_data_paths[idx] = copy.deepcopy(data_path)
- idx = idx + 1
+ if MonitoringByOnos == True:
+ exec_monitoring_by_onos(parsed_args)
+ else:
+ exec_processing_by_script(parsed_args)
- if MonitoringEnabled != True:
- break
- time.sleep(1)
diff --git a/web/ons-demo/RELEASE_NOTES.txt b/web/ons-demo/RELEASE_NOTES.txt
index 48834fb..8649e9b 100644
--- a/web/ons-demo/RELEASE_NOTES.txt
+++ b/web/ons-demo/RELEASE_NOTES.txt
@@ -1,4 +1,16 @@
** March 28, 2013 **
+- add and delete flow implemented
+- to add flow
+ - mouse down in src edge switch
+ - drag to dst edge switch
+ - release/confirm to create flow
+- to delete flow
+ - select flow in flow chooser
+ - click "close" button in flow table
+ - confirm
+- same "pending" style as other actions
+
+** March 28, 2013 **
- basic flow chooser
- click in "eye" to show full list
- click on "eye" in full list to monitor that flow in the top slot (and show the flow in topology)
diff --git a/web/ons-demo/assets/delete.svg b/web/ons-demo/assets/delete.svg
new file mode 100644
index 0000000..a32a278
--- /dev/null
+++ b/web/ons-demo/assets/delete.svg
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ width="283.465px"
+ height="283.465px"
+ viewBox="0 0 283.465 283.465"
+ enable-background="new 0 0 283.465 283.465"
+ xml:space="preserve"
+ inkscape:version="0.48.0 r9654"
+ sodipodi:docname="delete.svg"><metadata
+ id="metadata9"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs7" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="721"
+ inkscape:window-height="480"
+ id="namedview5"
+ showgrid="false"
+ inkscape:zoom="0.92427638"
+ inkscape:cx="141.7325"
+ inkscape:cy="141.7325"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="Layer_1" />
+<path
+ fill="#772953"
+ d="M141.733,198.425c-15.143,0-29.38-5.897-40.088-16.605c-10.708-10.708-16.605-24.945-16.605-40.088 c0-15.142,5.897-29.378,16.605-40.086c10.708-10.708,24.945-16.605,40.088-16.605c15.142,0,29.379,5.896,40.087,16.604 c10.708,10.708,16.605,24.946,16.605,40.089c0,15.143-5.897,29.381-16.605,40.088C171.112,192.528,156.875,198.425,141.733,198.425 L141.733,198.425z M141.733,99.213c-11.357,0-22.035,4.423-30.067,12.454c-8.031,8.031-12.454,18.708-12.454,30.064 c0,11.357,4.423,22.035,12.454,30.065c8.032,8.031,18.709,12.455,30.067,12.455c11.356,0,22.034-4.423,30.064-12.453 c8.031-8.03,12.455-18.709,12.455-30.066s-4.424-22.036-12.455-30.067C163.768,103.636,153.09,99.213,141.733,99.213L141.733,99.213 z M151.703,141.79l10.021-10.022c2.768-2.768,2.768-7.254,0-10.021c-2.767-2.767-7.255-2.767-10.021,0l-10.022,10.021 l-10.021-10.021c-2.767-2.767-7.255-2.767-10.022,0c-2.768,2.768-2.768,7.254,0,10.021l10.022,10.022l-10.023,10.023 c-2.768,2.768-2.768,7.254,0,10.021c1.383,1.384,3.197,2.076,5.011,2.076c1.813,0,3.627-0.692,5.011-2.076l10.023-10.023 l10.022,10.021c1.383,1.384,3.197,2.076,5.011,2.076s3.627-0.692,5.011-2.076c2.768-2.768,2.768-7.254,0-10.021L151.703,141.79z"
+ id="path3"
+ style="fill:#ffffff;fill-opacity:1" />
+</svg>
\ No newline at end of file
diff --git a/web/ons-demo/css/skin.default.css b/web/ons-demo/css/skin.default.css
index aebf68b..d88814e 100644
--- a/web/ons-demo/css/skin.default.css
+++ b/web/ons-demo/css/skin.default.css
@@ -61,6 +61,10 @@
border-bottom: 1px solid #AAA;
}
+.selectedFlow:hover {
+ background-color: #333;
+}
+
.selectedFlow:last-child {
border-bottom: none;
}
@@ -132,6 +136,11 @@
stroke: rgba(255, 255, 255, .35);
}
+path.flow.highlight {
+ stroke-width: 6px;
+ stroke: rgba(255, 255, 255, .75);
+}
+
#selectedFlowsHeader {
border-top: 1px solid #AAA;
}
@@ -142,7 +151,7 @@
}
-.flowId, .srcDPID, .dstDPID, .iperf {
+.flowId, .srcDPID, .dstDPID, .iperf, #deleteFlow, .deleteFlow {
display: -webkit-box;
-webkit-box-pack: center;
-webkit-box-align: center;
@@ -155,10 +164,14 @@
}
-.flowId, .srcDPID, .dstDPID {
+.srcDPID, .dstDPID, .deleteFlow, #deleteFlow {
border-right: 1px solid #AAA;
}
+.srcDPID {
+ border-left: 1px solid #AAA;
+}
+
#controllers {
border-right: 1px solid #AAA;
@@ -191,6 +204,14 @@
background-position: .25em center;
}
+.deleteFlow {
+ height: 100%;
+ background-image: url('../assets/delete.svg');
+ background-size: auto 150%;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
#logo {
height: 50px;
}
diff --git a/web/ons-demo/index.html b/web/ons-demo/index.html
index d157ecc..55237ac 100644
--- a/web/ons-demo/index.html
+++ b/web/ons-demo/index.html
@@ -38,6 +38,7 @@
</div>
</div>
<div id='selectedFlowsHeader'>
+ <div id='deleteFlow'></div>
<div id='showFlowChooser' class='flowId'><div class='white-eye'></div></div>
<div class='srcDPID'>src</div>
<div class='dstDPID'>dst</div>
diff --git a/web/ons-demo/js/app.js b/web/ons-demo/js/app.js
index 0fa5fa3..e342213 100644
--- a/web/ons-demo/js/app.js
+++ b/web/ons-demo/js/app.js
@@ -18,6 +18,9 @@
var svg;
var updateTopology;
var pendingLinks = {};
+var selectedFlows = [];
+
+var pendingTimeout = 10000;
var colors = [
'color1',
@@ -65,13 +68,9 @@
attr('id', 'viewbox').append('svg:g').attr('transform', 'translate(500 500)');
}
-var selectedFlows = [null, null, null];
-
-function drawFlows() {
+function updateSelectedFlowsTopology() {
// DRAW THE FLOWS
- var flows = d3.select('svg').selectAll('.flow').data(selectedFlows, function (d) {
- return d ? d.flowId.value : null;
- });
+ var flows = d3.select('svg').selectAll('.flow').data(selectedFlows);
flows.enter().append("svg:path").attr('class', 'flow')
.attr('stroke-dasharray', '4, 10')
@@ -85,27 +84,212 @@
flows.attr('d', function (d) {
- if (!d) {
- return;
- }
- var pts = [];
- d.dataPath.flowEntries.forEach(function (flowEntry) {
- var s = d3.select(document.getElementById(flowEntry.dpid.value));
- var pt = document.querySelector('svg').createSVGPoint();
- pt.x = s.attr('x');
- pt.y = s.attr('y');
- pt = pt.matrixTransform(s[0][0].getCTM());
- pts.push(pt);
+ if (!d) {
+ return;
+ }
+ var pts = [];
+ if (!d.dataPath.flowEntries) {
+ // create a temporary vector to indicate the pending flow
+ var s1 = d3.select(document.getElementById(d.dataPath.srcPort.dpid.value));
+ var s2 = d3.select(document.getElementById(d.dataPath.dstPort.dpid.value));
+
+ var pt1 = document.querySelector('svg').createSVGPoint();
+ pt1.x = s1.attr('x');
+ pt1.y = s1.attr('y');
+ pt1 = pt1.matrixTransform(s1[0][0].getCTM());
+ pts.push(pt1);
+
+ var pt2 = document.querySelector('svg').createSVGPoint();
+ pt2.x = s2.attr('x');
+ pt2.y = s2.attr('y');
+ pt2 = pt2.matrixTransform(s2[0][0].getCTM());
+ pts.push(pt2);
+
+ } else {
+ d.dataPath.flowEntries.forEach(function (flowEntry) {
+ var s = d3.select(document.getElementById(flowEntry.dpid.value));
+ // s[0] is null if the flow entry refers to a non-existent switch
+ if (s[0][0]) {
+ var pt = document.querySelector('svg').createSVGPoint();
+ pt.x = s.attr('x');
+ pt.y = s.attr('y');
+ pt = pt.matrixTransform(s[0][0].getCTM());
+ pts.push(pt);
+ } else {
+ console.log('flow refers to non-existent switch: ' + flowEntry.dpid.value);
+ }
+ });
+ }
+ return line(pts);
+ })
+ .attr('id', function (d) {
+ if (d) {
+ return makeFlowKey(d);
+ }
+ })
+ .classed('pending', function (d) {
+ return d && (d.createPending || d.deletePending);
});
- return line(pts);
- })
// "marching ants"
flows.select('animate').attr('from', 500);
+}
+
+function updateSelectedFlowsTable() {
+ function rowEnter(d) {
+ var row = d3.select(this);
+ row.append('div').classed('deleteFlow', true);
+ row.append('div').classed('flowId', true);
+ row.append('div').classed('srcDPID', true);
+ row.append('div').classed('dstDPID', true);
+ row.append('div').classed('iperf', true);
+
+ row.on('mouseover', function (d) {
+ if (d) {
+ var path = document.getElementById(makeFlowKey(d));
+ d3.select(path).classed('highlight', true);
+ }
+ });
+ row.on('mouseout', function (d) {
+ if (d) {
+ var path = document.getElementById(makeFlowKey(d));
+ d3.select(path).classed('highlight', false);
+ }
+ })
+ }
+
+ function rowUpdate(d) {
+ var row = d3.select(this);
+ row.select('.deleteFlow').on('click', function () {
+ if (d) {
+ var prompt = 'Delete flow ' + d.flowId.value + '?';
+ if (confirm(prompt)) {
+ deleteFlow(d);
+ d.deletePending = true;
+ updateSelectedFlows();
+
+ setTimeout(function () {
+ d.deletePending = false;
+ updateSelectedFlows();
+ }, pendingTimeout)
+ };
+ }
+ });
+
+ row.select('.flowId')
+ .text(function (d) {
+ if (d) {
+ if (d.flowId) {
+ return d.flowId.value;
+ } else {
+ return '0x--';
+ }
+ }
+ })
+ .classed('pending', d && (d.deletePending || d.createPending));
+
+ row.select('.srcDPID')
+ .text(function (d) {
+ if (d) {
+ return d.dataPath.srcPort.dpid.value;
+ }
+ });
+
+ row.select('.dstDPID')
+ .text(function (d) {
+ if (d) {
+ return d.dataPath.dstPort.dpid.value;
+ }
+ });
+ }
+
+ var flows = d3.select('#selectedFlows')
+ .selectAll('.selectedFlow')
+ .data(selectedFlows);
+
+ flows.enter()
+ .append('div')
+ .classed('selectedFlow', true)
+ .each(rowEnter);
+
+ flows.each(rowUpdate);
+
flows.exit().remove();
}
+function updateSelectedFlows() {
+ // make sure that all of the selected flows are either
+ // 1) valid (meaning they are in the latest list of flows)
+ // 2) pending
+ if (model) {
+ var flowMap = {};
+ model.flows.forEach(function (flow) {
+ flowMap[makeFlowKey(flow)] = flow;
+ });
+
+ var newSelectedFlows = [];
+ selectedFlows.forEach(function (flow) {
+ if (flow) {
+ var liveFlow = flowMap[makeFlowKey(flow)];
+ if (liveFlow) {
+ newSelectedFlows.push(liveFlow);
+ liveFlow.deletePending = flow.deletePending;
+ } else if (flow.createPending) {
+ newSelectedFlows.push(flow);
+ }
+ } else {
+ newSelectedFlows.push(null);
+ }
+ });
+ selectedFlows = newSelectedFlows;
+ }
+ while (selectedFlows.length < 3) {
+ selectedFlows.push(null);
+ }
+
+ updateSelectedFlowsTable();
+ updateSelectedFlowsTopology();
+}
+
+function selectFlow(flow) {
+ var flowKey = makeFlowKey(flow);
+ var alreadySelected = false;
+ selectedFlows.forEach(function (f) {
+ if (f && makeFlowKey(f) === flowKey) {
+ alreadySelected = true;
+ }
+ });
+
+ if (!alreadySelected) {
+ selectedFlows.unshift(flow);
+ selectedFlows = selectedFlows.slice(0, 3);
+ updateSelectedFlows();
+ }
+}
+
+function deselectFlow(flow, ifCreatePending) {
+ var flowKey = makeFlowKey(flow);
+ var newSelectedFlows = [];
+ selectedFlows.forEach(function (flow) {
+ if (!flow ||
+ flowKey !== makeFlowKey(flow) ||
+ flowKey === makeFlowKey(flow) && ifCreatePending && !flow.createPending ) {
+ newSelectedFlows.push(flow);
+ }
+ });
+ selectedFlows = newSelectedFlows;
+ while (selectedFlows.length < 3) {
+ selectedFlows.push(null);
+ }
+
+ updateSelectedFlows();
+}
+
+function deselectFlowIfCreatePending(flow) {
+ deselectFlow(flow, true);
+}
+
function showFlowChooser() {
function rowEnter(d) {
var row = d3.select(this);
@@ -113,11 +297,7 @@
row.append('div')
.classed('black-eye', true).
on('click', function () {
- selectedFlows.unshift(d);
- selectedFlows = selectedFlows.slice(0, 3);
-
- updateSelectedFlows();
- updateTopology();
+ selectFlow(d);
});
row.append('div')
@@ -159,54 +339,7 @@
}, 0);
}
-function updateSelectedFlows() {
- function rowEnter(d) {
- var row = d3.select(this);
- row.append('div').classed('flowId', true);
- row.append('div').classed('srcDPID', true);
- row.append('div').classed('dstDPID', true);
- row.append('div').classed('iperf', true);
- }
- function rowUpdate(d) {
- var row = d3.select(this);
- row.select('.flowId')
- .text(function (d) {
- if (d) {
- return d.flowId.value;
- }
- });
-
- row.select('.srcDPID')
- .text(function (d) {
- if (d) {
- return d.dataPath.srcPort.dpid.value;
- }
- });
-
- row.select('.dstDPID')
- .text(function (d) {
- if (d) {
- return d.dataPath.dstPort.dpid.value;
- }
- });
- }
-
- var flows = d3.select('#selectedFlows')
- .selectAll('.selectedFlow')
- .data(selectedFlows);
-
- flows.enter()
- .append('div')
- .classed('selectedFlow', true)
- .each(rowEnter);
-
- flows.each(rowUpdate);
-
- flows.exit().remove();
-
- return flows;
-}
function updateHeader(model) {
d3.select('#lastUpdate').text(new Date());
@@ -325,6 +458,10 @@
return link['src-switch'] + '=>' + link['dst-switch'];
}
+function makeFlowKey(flow) {
+ return flow.dataPath.srcPort.dpid.value + '=>' + flow.dataPath.dstPort.dpid.value;
+}
+
function createLinkMap(links) {
var linkMap = {};
links.forEach(function (link) {
@@ -361,7 +498,6 @@
var links = reconcilePendingLinks(model);
var linkMap = createLinkMap(links);
-// var flowMap = createFlowMap(model);
function mouseOverSwitch(data) {
@@ -650,9 +786,29 @@
if (s1Data.className == 'edge' && s2Data.className == 'edge') {
var prompt = 'Create flow from ' + srcData.dpid + ' to ' + dstData.dpid + '?';
if (confirm(prompt)) {
- alert('do create flow');
- } else {
- alert('do not create flow');
+ addFlow(srcData, dstData);
+
+ var flow = {
+ dataPath: {
+ srcPort: {
+ dpid: {
+ value: srcData.dpid
+ }
+ },
+ dstPort: {
+ dpid: {
+ value: dstData.dpid
+ }
+ }
+ },
+ createPending: true
+ };
+
+ selectFlow(flow);
+
+ setTimeout(function () {
+ deselectFlowIfCreatePending(flow);
+ }, pendingTimeout);
}
} else {
var map = linkMap[srcData.dpid];
@@ -697,7 +853,7 @@
delete pendingLinks[makeLinkKey(link2)];
updateTopology();
- }, 10000);
+ }, pendingTimeout);
}
}
}
@@ -830,9 +986,6 @@
// remove old links
links.exit().remove();
-
-
- drawFlows();
}
function updateControllers() {
diff --git a/web/ons-demo/js/controller.js b/web/ons-demo/js/controller.js
index a36ae1c..fd3f6ae 100644
--- a/web/ons-demo/js/controller.js
+++ b/web/ons-demo/js/controller.js
@@ -10,57 +10,64 @@
});
}
+function MAC(dpid) {
+ var cmps = dpid.split(':');
+ var MAC = '00:00:c0:a8:' + [cmps[6], cmps[7]].join(':');
+ return MAC;
+}
var controllerFunctions = {
- l: function (cmd, link) {
+ linkCmd: function (cmd, link) {
var url = '/proxy/gui/link/' + [cmd, link['src-switch'], link['src-port'], link['dst-switch'], link['dst-port']].join('/');
callURL(url);
},
- s: function (cmd, s) {
+ switchCmd: function (cmd, s) {
var url = '/proxy/gui/switch/' + [cmd, s.dpid].join('/');
callURL(url);
},
- c: function (cmd, c) {
+ ctrlCmd: function (cmd, c) {
var url = '/proxy/gui/controller/' + [cmd, c].join('/');
callURL(url);
+ },
+ addFlowCmd: function (src, dst) {
+ var url = '/proxy/gui/addflow/' + [src.dpid, 1, dst.dpid, 1, MAC(src.dpid), MAC(dst.dpid)].join('/');
+ callURL(url);
+ },
+ delFlowCmd: function (flow) {
+ var url = '/proxy/gui/delflow/' + flow.flowId.value;
+ callURL(url);
}
};
-
-// if (parseURLParameters().mock) {
-// urls = mockURLs;
-// }
-
-
function linkUp(link) {
- controllerFunctions.l('up', link);
+ controllerFunctions.linkCmd('up', link);
}
function linkDown(link) {
- controllerFunctions.l('down', link);
+ controllerFunctions.linkCmd('down', link);
}
function switchUp(s) {
- controllerFunctions.s('up', s);
+ controllerFunctions.switchCmd('up', s);
}
function switchDown(s) {
- controllerFunctions.s('down', s);
+ controllerFunctions.switchCmd('down', s);
}
function controllerUp(c) {
- controllerFunctions.c('up', c);
+ controllerFunctions.ctrlCmd('up', c);
}
function controllerDown(c) {
- controllerFunctions.c('down', c);
+ controllerFunctions.ctrlCmd('down', c);
}
-function createFlow(src, dst) {
-
+function addFlow(src, dst) {
+ controllerFunctions.addFlowCmd(src, dst);
}
-function deleteFlow(src, dst) {
-
+function deleteFlow(flow) {
+ controllerFunctions.delFlowCmd(flow);
}
\ No newline at end of file
diff --git a/web/topology_rest.py b/web/topology_rest.py
index f3a4a3b..0306de9 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -127,6 +127,31 @@
resp = Response(result, status=200, mimetype='application/json')
return resp
+@app.route("/proxy/gui/addflow/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>/<srcMAC>/<dstMAC>")
+def proxy_add_flow(src_dpid, src_port, dst_dpid, dst_port, srcMAC, dstMAC):
+ try:
+ command = "curl -s %s/gui/addflow/%s/%s/%s/%s/%s/%s" % (ONOS_GUI3_CONTROL_HOST, src_dpid, src_port, dst_dpid, dst_port, srcMAC, dstMAC)
+ print command
+ result = os.popen(command).read()
+ except:
+ print "REST IF has issue"
+ exit
+
+ resp = Response(result, status=200, mimetype='application/json')
+ return resp
+
+@app.route("/proxy/gui/delflow/<flow_id>")
+def proxy_del_flow(flow_id):
+ try:
+ command = "curl -s %s/gui/delflow/%s" % (ONOS_GUI3_CONTROL_HOST, flow_id)
+ print command
+ result = os.popen(command).read()
+ except:
+ print "REST IF has issue"
+ exit
+
+ resp = Response(result, status=200, mimetype='application/json')
+ return resp
@app.route("/wm/core/topology/switches/all/json")
def switches():
if request.args.get('proxy') == None:
@@ -729,7 +754,7 @@
cmd = 'up'
result=""
- for dpid in (src_dpid, dst_dpid):
+ for dpid in (src_dpid, dst_dpid):
if dpid in core_switches:
host = controllers[0]
src_ports = [1, 2, 3, 4, 5]