Merge remote-tracking branch 'upstream/master'
diff --git a/cassandra.titan b/cassandra.titan
index ef6f3ae..2f34fb3 100644
--- a/cassandra.titan
+++ b/cassandra.titan
@@ -1,3 +1,6 @@
storage.backend=cassandra
storage.hostname=localhost
storage.keyspace=onos
+storage.connection-pool-size=4096
+storage.replication-factor=3
+storage.read-consistency-level=1
diff --git a/scripts/link.sh b/scripts/link.sh
index d87464b..dc202e7 100755
--- a/scripts/link.sh
+++ b/scripts/link.sh
@@ -1,4 +1,5 @@
#! /bin/bash
+
controller=`hostname`
switches=`ifconfig -a | grep sw |grep -v eth | awk '{print $1}'`
@@ -20,21 +21,26 @@
for s in $switches; do
dpid=`sudo ovs-ofctl show $s |grep dpid | awk '{print $4}'`
if [ "x$dpid" == "x$src_dpid" ]; then
- intfs=`sudo ovs-ofctl show $s |grep addr | awk '{print $1}' | sed 's/.*(//g' | sed 's/):$//g'`
- intf_list=()
- for i in $intfs; do
- intf_list+=($i)
- done
- intf=${intf_list[$src_port]}
- if [ x$cmd == "xup" ]; then
- echo "sudo ifconfig ${intf} up"
- sudo ifconfig ${intf} up
- elif [ x$cmd == "xdown" ]; then
- echo "sudo ifconfig ${intf} down"
- sudo ifconfig ${intf} down
- else
- echo "sudo ifconfig ${intf}"
- sudo ifconfig ${intf}
+
+# intf=`sudo ovs-ofctl show $s |grep addr | awk -v p=$src_port 'BEGIN {pat="^ "p"\("}
+# $0 ~ pat {w=match ($0, /\(.*\)/); if (w) print substr($0, RSTART+1, RLENGTH-2)}'`
+
+ sudo ovs-ofctl show $s |grep addr | sed 's/[\(\)]/,/g'>/tmp/baz.out
+ intf=`cat /tmp/baz.out | awk -v p=$src_port 'BEGIN {pat="^ "p","}
+ $0 ~ pat {w=match($0, /,.*,/); if (w) print substr($0, RSTART+1, RLENGTH-2)}'`
+
+ if [ x$intf != "x" ]; then
+ if [ x$cmd == "xup" ]; then
+ echo "sudo ifconfig ${intf} up"
+ sudo ifconfig ${intf} up
+ elif [ x$cmd == "xdown" ]; then
+ echo "sudo ifconfig ${intf} down"
+ sudo ifconfig ${intf} down
+ else
+ echo "sudo ifconfig ${intf}"
+ sudo ifconfig ${intf}
+ fi
+ break
fi
fi
done
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index e58b19f..18da3dd 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -54,6 +54,10 @@
@JsonIgnore
@GremlinGroovy("_().out('on').out('host')")
public Iterable<IDeviceObject> getDevices();
+
+ @JsonIgnore
+ @Incidence(label="switch",direction = Direction.IN)
+ public Iterable<IFlowEntry> getFlowEntries();
}
public interface IPortObject extends IBaseObject{
@@ -94,6 +98,14 @@
@Adjacency(label="host")
public void removeDevice(final IDeviceObject device);
+ @JsonIgnore
+ @Incidence(label="inport",direction = Direction.IN)
+ public Iterable<IFlowEntry> getInFlowEntries();
+
+ @JsonIgnore
+ @Incidence(label="outport",direction = Direction.IN)
+ public Iterable<IFlowEntry> getOutFlowEntries();
+
// @JsonIgnore
// @Adjacency(label="link")
// public Iterable<ILinkObject> getLinks();
@@ -187,6 +199,10 @@
@Adjacency(label="flow", direction=Direction.IN)
public void removeFlowEntry(final IFlowEntry flowEntry);
+
+ @JsonIgnore
+ @GremlinGroovy("_().in('flow').out('switch')")
+ public Iterable<IDeviceObject> getSwitches();
}
public interface IFlowEntry extends IBaseObject {
@@ -267,5 +283,29 @@
@Property("actionOutput")
public void setActionOutput(Short actionOutput);
+
+ @Adjacency(label="flow")
+ public IFlowPath getFlow();
+
+ @Adjacency(label="flow")
+ public void setFlow(IFlowPath flow);
+
+ @Adjacency(label="switch")
+ public ISwitchObject getSwitch();
+
+ @Adjacency(label="switch")
+ public void setSwitch(ISwitchObject sw);
+
+ @Adjacency(label="inport")
+ public IPortObject getInPort();
+
+ @Adjacency(label="inport")
+ public void setInPort(IPortObject port);
+
+ @Adjacency(label="outport")
+ public IPortObject getOutPort();
+
+ @Adjacency(label="outport")
+ public void setOutPort(IPortObject port);
}
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 405af1a..2b23e18 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -5,6 +5,7 @@
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -22,6 +23,8 @@
import net.floodlightcontroller.core.INetMapStorage;
import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
+import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
@@ -48,6 +51,7 @@
import net.floodlightcontroller.util.OFMessageDamper;
import net.floodlightcontroller.util.Port;
import net.floodlightcontroller.util.SwitchPort;
+import net.onrc.onos.flow.IFlowManager;
import net.onrc.onos.util.GraphDBConnection;
import net.onrc.onos.util.GraphDBConnection.Transaction;
@@ -62,12 +66,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
+public class FlowManager implements IFloodlightModule, IFlowService, IFlowManager, INetMapStorage {
public GraphDBConnection conn;
protected IRestApiService restApi;
protected IFloodlightProviderService floodlightProvider;
+ protected ITopoRouteService topoRouteService;
protected FloodlightModuleContext context;
protected OFMessageDamper messageDamper;
@@ -142,8 +147,6 @@
return;
}
- ITopoRouteService topoRouteService =
- context.getServiceImpl(ITopoRouteService.class);
if (topoRouteService == null) {
log.debug("Topology Route Service not found");
return;
@@ -310,7 +313,7 @@
//
// Process my Flow Entries
//
- Boolean processed_measurement_flow = false;
+ boolean processed_measurement_flow = false;
for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
IFlowEntry flowEntryObj = entry.getValue();
// Code for measurement purpose
@@ -337,6 +340,7 @@
if (mySwitch == null)
continue; // Shouldn't happen
+ // TODO: PAVPAVPAV: FROM HERE...
//
// Create the Open Flow Flow Modification Entry to push
//
@@ -444,6 +448,9 @@
} catch (IOException e) {
log.error("Failure writing flow mod from network map", e);
}
+ // TODO: XXX: PAVPAVPAV: TO HERE.
+ // TODO: XXX: PAVPAVPAV: Update the flowEntryObj
+ // to "FE_SWITCH_UPDATED" in the new (refactored) code.
}
//
@@ -540,6 +547,7 @@
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
+ l.add(ITopoRouteService.class);
l.add(IRestApiService.class);
return l;
}
@@ -549,6 +557,7 @@
throws FloodlightModuleException {
this.context = context;
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ topoRouteService = context.getServiceImpl(ITopoRouteService.class);
restApi = context.getServiceImpl(IRestApiService.class);
messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
EnumSet.of(OFType.FLOW_MOD),
@@ -687,7 +696,11 @@
flowEntryObj.setType("flow_entry");
//
- // Set the Flow Entry attributes:
+ // Set the Flow Entry Edges and attributes:
+ // - Switch edge
+ // - InPort edge
+ // - OutPort edge
+ //
// - flowEntry.flowEntryMatch()
// - flowEntry.flowEntryActions()
// - flowEntry.dpid()
@@ -702,23 +715,42 @@
// - flowEntry.matchDstMac()
// - flowEntry.actionOutput()
//
+ ISwitchObject sw =
+ conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
- if (flowEntry.flowEntryMatch().matchInPort())
- flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
- if (flowEntry.flowEntryMatch().matchEthernetFrameType())
- flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
- if (flowEntry.flowEntryMatch().matchSrcIPv4Net())
- flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
- if (flowEntry.flowEntryMatch().matchDstIPv4Net())
- flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
- if (flowEntry.flowEntryMatch().matchSrcMac())
- flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
- if (flowEntry.flowEntryMatch().matchDstMac())
- flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
+ flowEntryObj.setSwitch(sw);
+ if (flowEntry.flowEntryMatch().matchInPort()) {
+ IPortObject inport =
+ conn.utils().searchPort(conn, flowEntry.dpid().toString(),
+ flowEntry.flowEntryMatch().inPort().value());
+ flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
+ flowEntryObj.setInPort(inport);
+ }
+ if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
+ flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
+ }
+ if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
+ flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
+ }
+ if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
+ flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
+ }
+ if (flowEntry.flowEntryMatch().matchSrcMac()) {
+ flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
+ }
+ if (flowEntry.flowEntryMatch().matchDstMac()) {
+ flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
+ }
for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
- if (fa.actionOutput() != null)
+ if (fa.actionOutput() != null) {
+ IPortObject outport =
+ conn.utils().searchPort(conn,
+ flowEntry.dpid().toString(),
+ fa.actionOutput().port().value());
flowEntryObj.setActionOutput(fa.actionOutput().port().value());
+ flowEntryObj.setOutPort(outport);
+ }
}
// TODO: Hacks with hard-coded state names!
if (found)
@@ -727,18 +759,16 @@
flowEntryObj.setUserState("FE_USER_ADD");
flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
//
- // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
- // and FlowEntryErrorState.
+ // TODO: Take care of the FlowEntryErrorState.
//
// Flow Entries edges:
// Flow
- // NextFE
- // InPort
- // OutPort
- // Switch
- if (! found)
+ // NextFE (TODO)
+ if (! found) {
flowObj.addFlowEntry(flowEntryObj);
+ flowEntryObj.setFlow(flowObj);
+ }
}
conn.endTx(Transaction.COMMIT);
@@ -1092,12 +1122,29 @@
//
// Extract the Flow state
//
- flowPath.setFlowId(new FlowId(flowObj.getFlowId()));
- flowPath.setInstallerId(new CallerId(flowObj.getInstallerId()));
- flowPath.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
- flowPath.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
- flowPath.dataPath().dstPort().setDpid(new Dpid(flowObj.getDstSwitch()));
- flowPath.dataPath().dstPort().setPort(new Port(flowObj.getDstPort()));
+ String flowIdStr = flowObj.getFlowId();
+ String installerIdStr = flowObj.getInstallerId();
+ String srcSwitchStr = flowObj.getSrcSwitch();
+ Short srcPortStr = flowObj.getSrcPort();
+ String dstSwitchStr = flowObj.getDstSwitch();
+ Short dstPortStr = flowObj.getDstPort();
+
+ if ((flowIdStr == null) ||
+ (installerIdStr == null) ||
+ (srcSwitchStr == null) ||
+ (srcPortStr == null) ||
+ (dstSwitchStr == null) ||
+ (dstPortStr == null)) {
+ // TODO: A work-around, becauuse of some bogus database objects
+ return null;
+ }
+
+ 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().dstPort().setDpid(new Dpid(dstSwitchStr));
+ flowPath.dataPath().dstPort().setPort(new Port(dstPortStr));
//
// Extract all Flow Entries
@@ -1105,8 +1152,20 @@
Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
for (IFlowEntry flowEntryObj : flowEntries) {
FlowEntry flowEntry = new FlowEntry();
- flowEntry.setFlowEntryId(new FlowEntryId(flowEntryObj.getFlowEntryId()));
- flowEntry.setDpid(new Dpid(flowEntryObj.getSwitchDpid()));
+ String flowEntryIdStr = flowEntryObj.getFlowEntryId();
+ String switchDpidStr = flowEntryObj.getSwitchDpid();
+ String userState = flowEntryObj.getUserState();
+ String switchState = flowEntryObj.getSwitchState();
+
+ if ((flowEntryIdStr == null) ||
+ (switchDpidStr == null) ||
+ (userState == null) ||
+ (switchState == null)) {
+ // TODO: A work-around, becauuse of some bogus database objects
+ continue;
+ }
+ flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
+ flowEntry.setDpid(new Dpid(switchDpidStr));
//
// Extract the match conditions
@@ -1143,10 +1202,7 @@
actions.add(action);
}
flowEntry.setFlowEntryActions(actions);
-
- String userState = flowEntryObj.getUserState();
flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
- String switchState = flowEntryObj.getSwitchState();
flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
//
// TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
@@ -1157,4 +1213,472 @@
return flowPath;
}
+
+ /**
+ * Add and maintain a shortest-path flow.
+ *
+ * NOTE: The Flow Path argument does NOT contain all flow entries.
+ * Instead, it contains a single dummy flow entry that is used to
+ * store the matching condition(s).
+ * That entry is replaced by the appropriate entries from the
+ * internally performed shortest-path computation.
+ *
+ * @param flowPath the Flow Path with the endpoints and the match
+ * conditions to install.
+ * @return the added shortest-path flow on success, otherwise null.
+ */
+ @Override
+ public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
+ //
+ // Do the shortest path computation
+ //
+ DataPath dataPath =
+ topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
+ flowPath.dataPath().dstPort());
+ if (dataPath == null)
+ return null;
+
+ FlowEntryMatch userFlowEntryMatch = null;
+ if (! flowPath.dataPath().flowEntries().isEmpty()) {
+ userFlowEntryMatch = flowPath.dataPath().flowEntries().get(0).flowEntryMatch();
+ }
+
+ //
+ // Set the incoming port matching and the outgoing port output
+ // actions for each flow entry.
+ //
+ for (FlowEntry flowEntry : dataPath.flowEntries()) {
+ // Set the incoming port matching
+ FlowEntryMatch flowEntryMatch = null;
+ if (userFlowEntryMatch != null)
+ flowEntryMatch = new FlowEntryMatch(userFlowEntryMatch);
+ else
+ flowEntryMatch = new FlowEntryMatch();
+ flowEntry.setFlowEntryMatch(flowEntryMatch);
+ flowEntryMatch.enableInPort(flowEntry.inPort());
+
+ // Set the outgoing port output action
+ ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
+ if (flowEntryActions == null) {
+ flowEntryActions = new ArrayList<FlowEntryAction>();
+ flowEntry.setFlowEntryActions(flowEntryActions);
+ }
+ FlowEntryAction flowEntryAction = new FlowEntryAction();
+ flowEntryAction.setActionOutput(flowEntry.outPort());
+ flowEntryActions.add(flowEntryAction);
+ }
+
+ //
+ // Prepare the computed Flow Path
+ //
+ FlowPath computedFlowPath = new FlowPath();
+ computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
+ computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
+ computedFlowPath.setDataPath(dataPath);
+
+ FlowId flowId = new FlowId();
+ if (! addFlow(computedFlowPath, flowId))
+ return null;
+
+ // TODO: Mark the flow for maintenance purpose
+
+ return (computedFlowPath);
+ }
+
+ /**
+ * Create a Flow from port to port.
+ *
+ * TODO: We don't need it for now.
+ *
+ * @param src_port the source port.
+ * @param dest_port the destination port.
+ */
+ @Override
+ public void createFlow(IPortObject src_port, IPortObject dest_port) {
+ // TODO: We don't need it for now.
+ }
+
+ /**
+ * Get all Flows matching a source and a destination port.
+ *
+ * TODO: Pankaj might be implementing it later.
+ *
+ * @param src_port the source port to match.
+ * @param dest_port the destination port to match.
+ * @return all flows matching the source and the destination port.
+ */
+ @Override
+ public Iterable<FlowPath> getFlows(IPortObject src_port,
+ IPortObject dest_port) {
+ // TODO: Pankaj might be implementing it later.
+ return null;
+ }
+
+ /**
+ * Get all Flows going out from a port.
+ *
+ * TODO: We need it now: Pankaj
+ *
+ * @param port the port to match.
+ * @return the list of flows that are going out from the port.
+ */
+ @Override
+ public Iterable<FlowPath> getFlows(IPortObject port) {
+ // TODO: We need it now: Pankaj
+ return null;
+ }
+
+ /**
+ * Reconcile all flows on inactive switch port.
+ *
+ * @param portObject the port that has become inactive.
+ */
+ @Override
+ public void reconcileFlows(IPortObject portObject) {
+ Iterable<IFlowEntry> inFlowEntries = portObject.getInFlowEntries();
+ Iterable<IFlowEntry> outFlowEntries = portObject.getOutFlowEntries();
+
+ //
+ // Collect all affected Flow IDs from the affected flow entries
+ //
+ HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
+ for (IFlowEntry flowEntryObj: inFlowEntries) {
+ IFlowPath flowObj = flowEntryObj.getFlow();
+ if (flowObj != null)
+ flowObjSet.add(flowObj);
+ }
+ for (IFlowEntry flowEntryObj: outFlowEntries) {
+ IFlowPath flowObj = flowEntryObj.getFlow();
+ if (flowObj != null)
+ flowObjSet.add(flowObj);
+ }
+ // conn.endTx(Transaction.COMMIT);
+
+ //
+ // Remove the old Flow Entries, and add the new Flow Entries
+ //
+ Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+ for (IFlowPath flowObj : flowObjSet) {
+ FlowPath flowPath = extractFlowPath(flowObj);
+ if (flowPath == null)
+ continue;
+
+ //
+ // Remove my Flow Entries from the Network MAP
+ //
+ Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+ for (IFlowEntry flowEntryObj : flowEntries) {
+ String dpidStr = flowEntryObj.getSwitchDpid();
+ if (dpidStr == null)
+ continue;
+ Dpid dpid = new Dpid(dpidStr);
+ IOFSwitch mySwitch = mySwitches.get(dpid.value());
+ if (mySwitch == null)
+ continue; // Ignore the entry: not my switch
+ flowObj.removeFlowEntry(flowEntryObj);
+ conn.utils().removeFlowEntry(conn, flowEntryObj);
+ }
+
+ //
+ // Delete the flow entries from the switches
+ //
+ for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+ flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
+ installFlowEntry(mySwitches, flowEntry);
+ }
+
+ //
+ // Calculate the new shortest path and install it in the
+ // Network MAP.
+ //
+ FlowPath addedFlowPath = addAndMaintainShortestPathFlow(flowPath);
+ if (addedFlowPath == null) {
+ log.error("Cannot add Shortest Path Flow from {} to {}",
+ flowPath.dataPath().srcPort().toString(),
+ flowPath.dataPath().dstPort().toString());
+ continue;
+ }
+
+ //
+ // Add the flow entries to the switches
+ //
+ for (FlowEntry flowEntry : addedFlowPath.dataPath().flowEntries()) {
+ flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
+ installFlowEntry(mySwitches, flowEntry);
+ }
+ }
+ }
+
+ /**
+ * Reconcile all flows between a source and a destination port.
+ *
+ * TODO: We don't need it for now.
+ *
+ * @param src_port the source port.
+ * @param dest_port the destination port.
+ */
+ @Override
+ public void reconcileFlow(IPortObject src_port, IPortObject dest_port) {
+ // TODO: We don't need it for now.
+ }
+
+ /**
+ * Compute the shortest path between a source and a destination ports.
+ *
+ * @param src_port the source port.
+ * @param dest_port the destination port.
+ * @return the computed shortest path between the source and the
+ * destination ports. The flow entries in the path itself would
+ * contain the incoming port matching and the outgoing port output
+ * actions set. However, the path itself will NOT have the Flow ID,
+ * Installer ID, and any additional matching conditions for the
+ * flow entries (e.g., source or destination MAC address, etc).
+ */
+ @Override
+ public FlowPath computeFlowPath(IPortObject src_port,
+ IPortObject dest_port) {
+ //
+ // Prepare the arguments
+ //
+ String dpidStr = src_port.getSwitch().getDPID();
+ Dpid srcDpid = new Dpid(dpidStr);
+ Port srcPort = new Port(src_port.getNumber());
+
+ dpidStr = dest_port.getSwitch().getDPID();
+ Dpid dstDpid = new Dpid(dpidStr);
+ Port dstPort = new Port(dest_port.getNumber());
+
+ SwitchPort src = new SwitchPort(srcDpid, srcPort);
+ SwitchPort dst = new SwitchPort(dstDpid, dstPort);
+
+ //
+ // Do the shortest path computation
+ //
+ DataPath dataPath = topoRouteService.getShortestPath(src, dst);
+ if (dataPath == null)
+ return null;
+
+ //
+ // Set the incoming port matching and the outgoing port output
+ // actions for each flow entry.
+ //
+ for (FlowEntry flowEntry : dataPath.flowEntries()) {
+ // Set the incoming port matching
+ FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
+ if (flowEntryMatch == null) {
+ flowEntryMatch = new FlowEntryMatch();
+ flowEntry.setFlowEntryMatch(flowEntryMatch);
+ }
+ flowEntryMatch.enableInPort(flowEntry.inPort());
+
+ // Set the outgoing port output action
+ ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
+ if (flowEntryActions == null) {
+ flowEntryActions = new ArrayList<FlowEntryAction>();
+ flowEntry.setFlowEntryActions(flowEntryActions);
+ }
+ FlowEntryAction flowEntryAction = new FlowEntryAction();
+ flowEntryAction.setActionOutput(flowEntry.outPort());
+ flowEntryActions.add(flowEntryAction);
+ }
+
+ //
+ // Prepare the return result
+ //
+ FlowPath flowPath = new FlowPath();
+ flowPath.setDataPath(dataPath);
+
+ return flowPath;
+ }
+
+ /**
+ * Get all Flow Entries of a Flow.
+ *
+ * @param flow the flow whose flow entries should be returned.
+ * @return the flow entries of the flow.
+ */
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(FlowPath flow) {
+ return flow.dataPath().flowEntries();
+ }
+
+ /**
+ * Install a Flow Entry on a switch.
+ *
+ * @param mySwitches the DPID-to-Switch mapping for the switches
+ * controlled by this controller.
+ * @param flowEntry the flow entry to install.
+ * @return true on success, otherwise false.
+ */
+ @Override
+ public boolean installFlowEntry(Map<Long, IOFSwitch> mySwitches,
+ FlowEntry flowEntry) {
+ IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
+ if (mySwitch == null) {
+ // Not my switch
+ return (installRemoteFlowEntry(flowEntry));
+ }
+
+ //
+ // Create the OpenFlow Flow Modification Entry to push
+ //
+ OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
+ .getMessage(OFType.FLOW_MOD);
+ long cookie = flowEntry.flowEntryId().value();
+
+ short flowModCommand = OFFlowMod.OFPFC_ADD;
+ if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
+ flowModCommand = OFFlowMod.OFPFC_ADD;
+ } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
+ flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
+ } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
+ flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
+ } else {
+ // Unknown user state. Ignore the entry
+ log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
+ flowEntry.flowEntryId().toString(),
+ flowEntry.flowEntryUserState());
+ return false;
+ }
+
+ //
+ // Fetch the match conditions
+ //
+ OFMatch match = new OFMatch();
+ match.setWildcards(OFMatch.OFPFW_ALL);
+ Port matchInPort = flowEntry.flowEntryMatch().inPort();
+ if (matchInPort != null) {
+ match.setInputPort(matchInPort.value());
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
+ }
+ Short matchEthernetFrameType =
+ flowEntry.flowEntryMatch().ethernetFrameType();
+ if (matchEthernetFrameType != null) {
+ match.setDataLayerType(matchEthernetFrameType);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+ }
+ IPv4Net matchSrcIPv4Net = flowEntry.flowEntryMatch().srcIPv4Net();
+ if (matchSrcIPv4Net != null) {
+ match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
+ }
+ IPv4Net matchDstIPv4Net = flowEntry.flowEntryMatch().dstIPv4Net();
+ if (matchDstIPv4Net != null) {
+ match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
+ }
+ MACAddress matchSrcMac = flowEntry.flowEntryMatch().srcMac();
+ if (matchSrcMac != null) {
+ match.setDataLayerSource(matchSrcMac.toString());
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
+ }
+ MACAddress matchDstMac = flowEntry.flowEntryMatch().dstMac();
+ if (matchDstMac != null) {
+ match.setDataLayerDestination(matchDstMac.toString());
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+ }
+
+ //
+ // Fetch the actions
+ //
+ // TODO: For now we support only the "OUTPUT" actions.
+ //
+ fm.setOutPort(OFPort.OFPP_NONE.getValue());
+ List<OFAction> actions = new ArrayList<OFAction>();
+ ArrayList<FlowEntryAction> flowEntryActions =
+ flowEntry.flowEntryActions();
+ for (FlowEntryAction flowEntryAction : flowEntryActions) {
+ FlowEntryAction.ActionOutput actionOutput =
+ flowEntryAction.actionOutput();
+ if (actionOutput != null) {
+ short actionOutputPort = actionOutput.port().value();
+ OFActionOutput action = new OFActionOutput();
+ // XXX: The max length is hard-coded for now
+ action.setMaxLength((short)0xffff);
+ action.setPort(actionOutputPort);
+ actions.add(action);
+ if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
+ (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
+ fm.setOutPort(actionOutputPort);
+ }
+ }
+ }
+
+ fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+ .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+ .setPriority(PRIORITY_DEFAULT)
+ .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+ .setCookie(cookie)
+ .setCommand(flowModCommand)
+ .setMatch(match)
+ .setActions(actions)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+
+ //
+ // TODO: Set the following flag
+ // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
+ // See method ForwardingBase::pushRoute()
+ //
+
+ //
+ // Write the message to the switch
+ //
+ try {
+ messageDamper.write(mySwitch, fm, null);
+ mySwitch.flush();
+ } catch (IOException e) {
+ log.error("Failure writing flow mod from network map", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Remove a Flow Entry from a switch.
+ *
+ * @param mySwitches the DPID-to-Switch mapping for the switches
+ * controlled by this controller.
+ * @param flowEntry the flow entry to remove.
+ * @return true on success, otherwise false.
+ */
+ @Override
+ public boolean removeFlowEntry(Map<Long, IOFSwitch> mySwitches,
+ FlowEntry flowEntry) {
+ //
+ // The installFlowEntry() method implements both installation
+ // and removal of flow entries.
+ //
+ return (installFlowEntry(mySwitches, flowEntry));
+ }
+
+ /**
+ * Install a Flow Entry on a remote controller.
+ *
+ * TODO: We need it now: Jono
+ * - For now it will make a REST call to the remote controller.
+ * - Internally, it needs to know the name of the remote controller.
+ *
+ * @param flowEntry the flow entry to install.
+ * @return true on success, otherwise false.
+ */
+ @Override
+ public boolean installRemoteFlowEntry(FlowEntry flowEntry) {
+ // TODO: We need it now: Jono
+ // - For now it will make a REST call to the remote controller.
+ // - Internally, it needs to know the name of the remote controller.
+ return true;
+ }
+
+ /**
+ * Remove a flow entry on a remote controller.
+ *
+ * @param flowEntry the flow entry to remove.
+ * @return true on success, otherwise false.
+ */
+ @Override
+ public boolean removeRemoteFlowEntry(FlowEntry flowEntry) {
+ //
+ // The installRemoteFlowEntry() method implements both installation
+ // and removal of flow entries.
+ //
+ return (installRemoteFlowEntry(flowEntry));
+ }
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 41c0f57..b6df1e2 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -82,4 +82,19 @@
* @return the Flow Paths if found, otherwise null.
*/
ArrayList<FlowPath> getAllFlows();
+
+ /**
+ * Add and maintain a shortest-path flow.
+ *
+ * NOTE: The Flow Path argument does NOT contain all flow entries.
+ * Instead, it contains a single dummy flow entry that is used to
+ * store the matching condition(s).
+ * That entry is replaced by the appropriate entries from the
+ * internally performed shortest-path computation.
+ *
+ * @param flowPath the Flow Path with the endpoints and the match
+ * conditions to install.
+ * @return the added shortest-path flow on success, otherwise null.
+ */
+ public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/AddShortestPathFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/AddShortestPathFlowResource.java
new file mode 100644
index 0000000..3c5bcd8
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/AddShortestPathFlowResource.java
@@ -0,0 +1,65 @@
+package net.floodlightcontroller.flowcache.web;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+import net.floodlightcontroller.util.FlowPath;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.restlet.resource.Post;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AddShortestPathFlowResource extends ServerResource {
+
+ protected static Logger log = LoggerFactory.getLogger(AddShortestPathFlowResource.class);
+
+ @Post("json")
+ public FlowId store(String flowJson) {
+ FlowId result = new FlowId();
+
+ IFlowService flowService =
+ (IFlowService)getContext().getAttributes().
+ get(IFlowService.class.getCanonicalName());
+
+ if (flowService == null) {
+ log.debug("ONOS Flow Service not found");
+ return result;
+ }
+
+ //
+ // Extract the arguments
+ // NOTE: The "flow" is specified in JSON format.
+ //
+ ObjectMapper mapper = new ObjectMapper();
+ String flowPathStr = flowJson;
+ FlowPath flowPath = null;
+ log.debug("Add Shortest Path Flow Path: " + flowPathStr);
+ try {
+ flowPath = mapper.readValue(flowPathStr, FlowPath.class);
+ } catch (JsonGenerationException e) {
+ e.printStackTrace();
+ } catch (JsonMappingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // Process the request
+ if (flowPath != null) {
+ FlowPath addedFlowPath =
+ flowService.addAndMaintainShortestPathFlow(flowPath);
+ if (addedFlowPath == null)
+ result = new FlowId(); // Error: Return empty Flow Id
+ else
+ result = addedFlowPath.flowId();
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index 19f9e14..962dbbb 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -14,6 +14,7 @@
public Restlet getRestlet(Context context) {
Router router = new Router(context);
router.attach("/add/json", AddFlowResource.class);
+ router.attach("/add-shortest-path/json", AddShortestPathFlowResource.class);
router.attach("/clear/{flow-id}/json", ClearFlowResource.class);
router.attach("/delete/{flow-id}/json", DeleteFlowResource.class);
router.attach("/get/{flow-id}/json", GetFlowByIdResource.class);
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntry.java b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
index 56a1631..2e61636 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntry.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
@@ -23,7 +23,7 @@
* support multiple in-ports and multiple out-ports.
*/
public class FlowEntry {
- private FlowId flowId; //FlowID of flowEntry
+ private FlowId flowId; // FlowID of flowEntry
private FlowEntryId flowEntryId; // The Flow Entry ID
private FlowEntryMatch flowEntryMatch; // The Flow Entry Match
private ArrayList<FlowEntryAction> flowEntryActions; // The Flow Entry Actions
@@ -127,6 +127,22 @@
}
/**
+ * Get the Flow ID.
+ * @return the Flow ID.
+ */
+ @JsonIgnore
+ public FlowId getFlowId() { return flowId; }
+
+ /**
+ * Set the Flow ID.
+ *
+ * @param flowId the Flow ID to set.
+ */
+ public void setFlowId(FlowId flowId) {
+ this.flowId = flowId;
+ }
+
+ /**
* Get the Flow Entry ID.
*
* @return the Flow Entry ID.
@@ -329,19 +345,4 @@
return ret;
}
-
- /**
- * @return the flowId
- */
- @JsonIgnore
- public FlowId getFlowId() {
- return flowId;
- }
-
- /**
- * @param flowId the flowId to set
- */
- public void setFlowId(FlowId flowId) {
- this.flowId = flowId;
- }
}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java b/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
index 64527c5..6c8e71e 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
@@ -27,6 +27,8 @@
/**
* Constructor for a given value to match.
+ *
+ * @param value the value to match.
*/
public Field(T value) {
this.value = value;
@@ -88,6 +90,38 @@
}
/**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public FlowEntryMatch(FlowEntryMatch other) {
+ if ((other.inPort != null) && other.inPort.enabled())
+ this.enableInPort(other.inPort.value());
+ if ((other.srcMac != null) && other.srcMac.enabled())
+ this.enableSrcMac(other.srcMac.value());
+ if ((other.dstMac != null) && other.dstMac.enabled())
+ this.enableDstMac(other.dstMac.value());
+ if ((other.vlanId != null) && other.vlanId.enabled())
+ this.enableVlanId(other.vlanId.value());
+ if ((other.vlanPriority != null) && other.vlanPriority.enabled())
+ this.enableVlanPriority(other.vlanPriority.value());
+ if ((other.ethernetFrameType != null) && other.ethernetFrameType.enabled())
+ this.enableEthernetFrameType(other.ethernetFrameType.value());
+ if ((other.ipToS != null) && other.ipToS.enabled())
+ this.enableIpToS(other.ipToS.value());
+ if ((other.ipProto != null) && other.ipProto.enabled())
+ this.enableIpProto(other.ipProto.value());
+ if ((other.srcIPv4Net != null) && other.srcIPv4Net.enabled())
+ this.enableSrcIPv4Net(other.srcIPv4Net.value());
+ if ((other.dstIPv4Net != null) && other.dstIPv4Net.enabled())
+ this.enableDstIPv4Net(other.dstIPv4Net.value());
+ if ((other.srcTcpUdpPort != null) && other.srcTcpUdpPort.enabled())
+ this.enableSrcTcpUdpPort(other.srcTcpUdpPort.value());
+ if ((other.dstTcpUdpPort != null) && other.dstTcpUdpPort.enabled())
+ this.enableDstTcpUdpPort(other.dstTcpUdpPort.value());
+ }
+
+ /**
* Get the matching input switch port.
*
* @return the matching input switch port.
diff --git a/src/main/java/net/onrc/onos/flow/IFlowManager.java b/src/main/java/net/onrc/onos/flow/IFlowManager.java
index da6448c..8394046 100644
--- a/src/main/java/net/onrc/onos/flow/IFlowManager.java
+++ b/src/main/java/net/onrc/onos/flow/IFlowManager.java
@@ -1,53 +1,124 @@
package net.onrc.onos.flow;
+import java.util.Map;
+
+import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
import net.floodlightcontroller.util.FlowEntry;
import net.floodlightcontroller.util.FlowPath;
public interface IFlowManager {
-
- /*
- * Generic create Flow from port to port
- */
- public void createFlow(IPortObject src_port, IPortObject dest_port);
- /*
- * get Flows matching a src_port & dest_port
- */
- public Iterable<FlowPath> getFlows(IPortObject src_port, IPortObject dest_port);
- /*
- * get all Flows going out from port
- */
- public Iterable<FlowPath> getFlows(IPortObject port);
- /*
- * Reconcile all flows on inactive port (src port of link which might be broken)
- */
- public void reconcileFlows(IPortObject src_port);
- /*
- * Reconcile flow based on flow
- */
- public void reconcileFlow(IPortObject src_port, IPortObject dest_port);
- /*
- * compute a flow path using src/dest port
- */
- public FlowPath computeFlowPath(IPortObject src_port, IPortObject dest_port);
- /*
- * Get all FlowEntries of a Flow
- */
+ /**
+ * Create a Flow from port to port.
+ *
+ * TODO: We don't need it for now.
+ *
+ * @param src_port the source port.
+ * @param dest_port the destination port.
+ */
+ public void createFlow(IPortObject src_port, IPortObject dest_port);
+
+ /**
+ * Get all Flows matching a source and a destination port.
+ *
+ * TODO: Pankaj might be implementing it later.
+ *
+ * @param src_port the source port to match.
+ * @param dest_port the destination port to match.
+ * @return all flows matching the source and the destination port.
+ */
+ public Iterable<FlowPath> getFlows(IPortObject src_port,
+ IPortObject dest_port);
+
+ /**
+ * Get all Flows going out from a port.
+ *
+ * TODO: We need it now: Pankaj
+ *
+ * @param port the port to match.
+ * @return the list of flows that are going out from the port.
+ */
+ public Iterable<FlowPath> getFlows(IPortObject port);
+
+ /**
+ * Reconcile all flows on inactive switch port.
+ *
+ * @param portObject the port that has become inactive.
+ */
+ public void reconcileFlows(IPortObject portObject);
+
+ /**
+ * Reconcile all flows between a source and a destination port.
+ *
+ * TODO: We don't need it for now.
+ *
+ * @param src_port the source port.
+ * @param dest_port the destination port.
+ */
+ public void reconcileFlow(IPortObject src_port, IPortObject dest_port);
+
+ /**
+ * Compute the shortest path between a source and a destination ports.
+ *
+ * @param src_port the source port.
+ * @param dest_port the destination port.
+ * @return the computed shortest path between the source and the
+ * destination ports. The flow entries in the path itself would
+ * contain the incoming port matching and the outgoing port output
+ * actions set. However, the path itself will NOT have the Flow ID,
+ * Installer ID, and any additional matching conditions for the
+ * flow entries (e.g., source or destination MAC address, etc).
+ */
+ public FlowPath computeFlowPath(IPortObject src_port,
+ IPortObject dest_port);
+
+ /**
+ * Get all Flow Entries of a Flow.
+ *
+ * @param flow the flow whose flow entries should be returned.
+ * @return the flow entries of the flow.
+ */
public Iterable<FlowEntry> getFlowEntries(FlowPath flow);
- /*
- * install a flow entry on switch
+
+ /**
+ * Install a Flow Entry on a switch.
+ *
+ * @param mySwitches the DPID-to-Switch mapping for the switches
+ * controlled by this controller.
+ * @param flowEntry the flow entry to install.
+ * @return true on success, otherwise false.
*/
- public void installFlowEntry(FlowEntry entry);
- /*
- * remove a flowEntry from switch
+ public boolean installFlowEntry(Map<Long, IOFSwitch> mySwitches,
+ FlowEntry flowEntry);
+
+ /**
+ * Remove a Flow Entry from a switch.
+ *
+ * @param mySwitches the DPID-to-Switch mapping for the switches
+ * controlled by this controller.
+ * @param flowEntry the flow entry to remove.
+ * @return true on success, otherwise false.
*/
- public void removeFlowEntry(FlowEntry entry);
- /*
- * install flow entry on remote controller
+ public boolean removeFlowEntry(Map<Long, IOFSwitch> mySwitches,
+ FlowEntry flowEntry);
+
+ /**
+ * Install a Flow Entry on a remote controller.
+ *
+ * TODO: We need it now: Jono
+ * - For now it will make a REST call to the remote controller.
+ * - Internally, it needs to know the name of the remote controller.
+ *
+ * @param flowEntry the flow entry to install.
+ * @return true on success, otherwise false.
*/
- public void installFlowEntry(String ctrlId, FlowEntry entry);
- /*
- * remove flow entry on remote controller
+ public boolean installRemoteFlowEntry(FlowEntry flowEntry);
+
+ /**
+ * Remove a flow entry on a remote controller.
+ *
+ * @param flowEntry the flow entry to remove.
+ * @return true on success, otherwise false.
*/
- public void removeFlowEntry(String ctrlId, FlowEntry entry);
+ public boolean removeRemoteFlowEntry(FlowEntry flowEntry);
}
diff --git a/web/ons-demo/data/configuration.json.dev b/web/ons-demo/data/configuration.json.dev
new file mode 100644
index 0000000..90cad6a
--- /dev/null
+++ b/web/ons-demo/data/configuration.json.dev
@@ -0,0 +1,40 @@
+{
+ "core": [
+ "00:00:00:00:00:00:01:01",
+ "00:00:00:00:00:00:01:02",
+ "00:00:00:00:00:00:01:03",
+ "00:00:00:00:00:00:01:04",
+ "00:00:00:00:00:00:01:05",
+ "00:00:00:00:00:00:01:06"
+ ],
+ "aggregation": [
+ "00:00:00:00:00:00:02:01",
+ "00:00:00:00:00:00:03:01",
+ "00:00:00:00:00:00:04:01",
+ "00:00:00:00:00:00:05:01",
+ "00:00:00:00:00:00:06:01",
+ "00:00:00:00:00:00:07:01",
+ "00:00:00:00:00:00:08:01"
+ ],
+ "association": {
+ "00:00:00:00:00:00:01:01": [
+ "00:00:00:00:00:00:03:01"
+ ],
+ "00:00:00:00:00:00:01:02": [
+ "00:00:00:00:00:00:02:01"
+ ],
+ "00:00:00:00:00:00:01:03": [
+ "00:00:00:00:00:00:07:01"
+ ],
+ "00:00:00:00:00:00:01:04": [
+ "00:00:00:00:00:00:04:01",
+ "00:00:00:00:00:00:05:01"
+ ],
+ "00:00:00:00:00:00:01:05": [
+ "00:00:00:00:00:00:06:01"
+ ],
+ "00:00:00:00:00:00:01:06": [
+ "00:00:00:00:00:00:08:01"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/web/ons-demo/data/controllers.json.dev b/web/ons-demo/data/controllers.json.dev
new file mode 100644
index 0000000..4abcf5a
--- /dev/null
+++ b/web/ons-demo/data/controllers.json.dev
@@ -0,0 +1,10 @@
+[
+ "onosdevb1",
+ "onosdevb2",
+ "onosdevb3",
+ "onosdevb4",
+ "onosdevb5",
+ "onosdevb6",
+ "onosdevb7",
+ "onosdevb8"
+]
\ No newline at end of file
diff --git a/web/topology_rest.py b/web/topology_rest.py
index 8a94ef9..40e36ae 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -16,9 +16,22 @@
RestIP="localhost"
RestPort=8080
#DBName="onos-network-map"
-#controllers=["onosgui1", "onosgui2", "onosgui3", "onosgui4"]
+
+## Uncomment the desired block based on your testbed environment
+
+# Settings for running on production
controllers=["onosgui1", "onosgui2", "onosgui3", "onosgui4", "onosgui5", "onosgui6", "onosgui7", "onosgui8"]
core_switches=["00:00:00:00:ba:5e:ba:11", "00:00:00:00:00:00:ba:12", "00:00:20:4e:7f:51:8a:35", "00:00:00:00:ba:5e:ba:13", "00:00:00:08:a2:08:f9:01", "00:00:00:16:97:08:9a:46"]
+ONOS_GUI3_HOST="http://gui3.onlab.us:8080"
+ONOS_GUI3_CONTROL_HOST="http://gui3.onlab.us:8081"
+
+# Settings for running on dev testbed. Replace dev
+#controllers=["onosdevb1", "onosdevb2", "onosdevb3", "onosdevb4"]
+#core_switches=["00:00:00:00:00:00:01:01", "00:00:00:00:00:00:01:02", "00:00:00:00:00:00:01:03", "00:00:00:00:00:00:01:04", "00:00:00:00:00:00:01:05", "00:00:00:00:00:00:01:06"]
+#ONOS_GUI3_HOST="http://devb-gui.onlab.us:8080"
+#ONOS_GUI3_CONTROL_HOST="http://devb-gui.onlab.us:8080"
+
+ONOS_LOCAL_HOST="http://localhost:8080" ;# for Amazon EC2
nr_flow=0
@@ -74,10 +87,6 @@
return response
-## PROXY API (allows development where the webui is served from someplace other than the controller)##
-ONOS_GUI3_HOST="http://gui3.onlab.us:8080"
-ONOS_GUI3_CONTROL_HOST="http://gui3.onlab.us:8081"
-ONOS_LOCAL_HOST="http://localhost:8080" ;# for Amazon EC2
@app.route("/proxy/gui/link/<cmd>/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>")
def proxy_link_change(cmd, src_dpid, src_port, dst_dpid, dst_port):
@@ -686,9 +695,11 @@
stop_onos="ssh -i ~/.ssh/onlabkey.pem %s ONOS/start-onos.sh stop" % (controller_name)
if cmd == "up":
+ print start_onos
result=os.popen(start_onos).read()
ret = "controller %s is up" % (controller_name)
elif cmd == "down":
+ print stop_onos
result=os.popen(stop_onos).read()
ret = "controller %s is down" % (controller_name)
@@ -698,8 +709,9 @@
def switch_status_change(cmd, dpid):
r = re.compile(':')
dpid = re.sub(r, '', dpid)
- cmd_string="ssh -i ~/.ssh/onlabkey.pem onosgui1 'cd ONOS/scripts; ./switch.sh %s %s'" % (dpid, cmd)
- get_status="ssh -i ~/.ssh/onlabkey.pem onosgui1 'cd ONOS/scripts; ./switch.sh %s'" % (dpid)
+ host=controllers[0]
+ cmd_string="ssh -i ~/.ssh/onlabkey.pem %s 'cd ONOS/scripts; ./switch.sh %s %s'" % (host, dpid, cmd)
+ get_status="ssh -i ~/.ssh/onlabkey.pem %s 'cd ONOS/scripts; ./switch.sh %s'" % (host, dpid)
print "cmd_string"
if cmd =="up" or cmd=="down":
@@ -709,12 +721,40 @@
return result
-#* Link Up/Down
+#* Link Up
#http://localhost:9000/gui/link/up/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>
-#http://localhost:9000/gui/link/down/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>
+@app.route("/gui/link/up/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>")
+def link_up(src_dpid, src_port, dst_dpid, dst_port):
+ cmd = 'up'
+ result=""
+
+ for dpid in (src_dpid, dst_dpid):
+ if dpid in core_switches:
+ host = controllers[0]
+ src_ports = [1, 2, 3, 4, 5]
+ else:
+ hostid=int(src_dpid.split(':')[-2])
+ host = controllers[hostid-1]
+ if hostid == 2 :
+ src_ports = [51]
+ else :
+ src_ports = [26]
+
+ for port in src_ports :
+ cmd_string="ssh -i ~/.ssh/onlabkey.pem %s 'cd ONOS/scripts; ./link.sh %s %s %s'" % (host, dpid, port, cmd)
+ print cmd_string
+ res=os.popen(cmd_string).read()
+ result = result + ' ' + res
+
+ return result
+
+
+#* Link Down
+#http://localhost:9000/gui/link/down/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>
@app.route("/gui/link/<cmd>/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>")
-def link_change(cmd, src_dpid, src_port, dst_dpid, dst_port):
+def link_down(cmd, src_dpid, src_port, dst_dpid, dst_port):
+
if src_dpid in core_switches:
host = controllers[0]
else:
@@ -724,8 +764,7 @@
cmd_string="ssh -i ~/.ssh/onlabkey.pem %s 'cd ONOS/scripts; ./link.sh %s %s %s'" % (host, src_dpid, src_port, cmd)
print cmd_string
- if cmd =="up" or cmd=="down":
- result=os.popen(cmd_string).read()
+ result=os.popen(cmd_string).read()
return result
@@ -758,10 +797,10 @@
return errcode
#* Start Iperf Througput
-#http://localhost:9000/gui/iperf/start/<flow_id>
-@app.route("/gui/iperf/start/<flow_id>")
-def iperf_start(flow_id):
- command = "iperf -xCMSV -t30 -i1 -u -c 127.0.0.1 > iperf_%s.out &" % (flow_id)
+#http://localhost:9000/gui/iperf/start/<flow_id>/<duration>
+@app.route("/gui/iperf/start/<flow_id>/<duration>")
+def iperf_start(flow_id,duration):
+ command = "iperf -xCMSV -t%d -i 0.5 -y c -u -c 127.0.0.1 > iperf_%s.out 2>/dev/null &" % (duration, flow_id)
print command
errcode = os.popen(command).read()
return errcode