Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS
diff --git a/scripts/ctrl-one.sh b/scripts/ctrl-one.sh
new file mode 100755
index 0000000..207d3f2
--- /dev/null
+++ b/scripts/ctrl-one.sh
@@ -0,0 +1,26 @@
+#! /bin/bash
+
+if [ "x$1" == "x" ];
+then
+ echo "No controller specified"
+ exit 1
+fi
+
+#controller=`hostname`
+controller=$1
+switches=`ifconfig -a | grep sw |grep -v eth | awk '{print $1}'`
+
+function host2ip (){
+ ip=`grep $1 /etc/hosts |grep -v "ip6"| awk '{print $1}'`
+ echo $ip
+}
+
+url=""
+for c in $controller; do
+ url="$url tcp:`host2ip $c`:6633"
+done
+echo $url
+for s in $switches; do
+ echo "set switch $s controller $url"
+ sudo ovs-vsctl set-controller $s $url
+done
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
index fd7c364..773083b 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);
}
/**
@@ -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/onrc/onos/util/GraphDBUtils.java b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
index 2029d80..c43434f 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
@@ -45,6 +45,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/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 40e36ae..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]
@@ -771,7 +796,7 @@
#* Create Flow
#http://localhost:9000/gui/addflow/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>/<srcMAC>/<dstMAC>
#1 FOOBAR 00:00:00:00:00:00:01:01 1 00:00:00:00:00:00:01:0b 1 matchSrcMac 00:00:00:00:00:00 matchDstMac 00:01:00:00:00:00
-@app.route("/gui/addfow/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>/<srcMAC>/<dstMAC>")
+@app.route("/gui/addflow/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>/<srcMAC>/<dstMAC>")
def add_flow(src_dpid, src_port, dst_dpid, dst_port, srcMAC, dstMAC):
command = "/home/ubuntu/ONOS/web/get_flow.py all |grep FlowPath |gawk '{print strtonum($4)}'| sort -n | tail -n 1"
print command