* Added initial support for add/get/delete Flow state to the Network MAP via the REST API
NOTE: The "add" REST API can't be used as-is.
* Added initial support for reading the Flow state from the Network MAP by the Controller
and sending it to the switches.
Currently, the Controller reads periodically the Flow entries (every 3 seconds)
NOTE: The writing of the OpenFlow state to the switches is not tested.
The Python scripts for to add/delete/get flows are intentionally omitted until
the "add" REST API issue is resolved.
NOTE: Two new keys have been added to the database: "flow_id" and "flow_entry_id".
This requires that the older database should be deleted, because Cassandra
doesn't allow adding new keys to an existing database.
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index 4af1deb..8788aa7 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -132,4 +132,101 @@
@GremlinGroovy("_().in('host').in('on').path(){it.number}{it.dpid}")
public Iterable<SwitchPort> getAttachmentPoints();*/
}
+
+public interface IFlowPath extends IBaseObject {
+ @Property("flow_id")
+ public String getFlowId();
+
+ @Property("flow_id")
+ public void setFlowId(String flowId);
+
+ @Property("installer_id")
+ public String getInstallerId();
+
+ @Property("installer_id")
+ public void setInstallerId(String installerId);
+
+ @Property("src_switch")
+ public String getSrcSwitch();
+
+ @Property("src_switch")
+ public void setSrcSwitch(String srcSwitch);
+
+ @Property("src_port")
+ public Short getSrcPort();
+
+ @Property("src_port")
+ public void setSrcPort(Short srcPort);
+
+ @Property("dst_switch")
+ public String getDstSwitch();
+
+ @Property("dst_switch")
+ public void setDstSwitch(String dstSwitch);
+
+ @Property("dst_port")
+ public Short getDstPort();
+
+ @Property("dst_port")
+ public void setDstPort(Short dstPort);
+
+ @Adjacency(label="flow", direction=Direction.IN)
+ public Iterable<IFlowEntry> getFlowEntries();
+
+ @Adjacency(label="flow", direction=Direction.IN)
+ public void addFlowEntry(final IFlowEntry flowEntry);
+
+ @Adjacency(label="flow", direction=Direction.IN)
+ public void removeFlowEntry(final IFlowEntry flowEntry);
+ }
+
+public interface IFlowEntry extends IBaseObject {
+ @Property("flow_entry_id")
+ public String getFlowEntryId();
+
+ @Property("flow_entry_id")
+ public void setFlowEntryId(String flowEntryId);
+
+ @Property("switch_dpid")
+ public String getSwitchDpid();
+
+ @Property("switch_dpid")
+ public void setSwitchDpid(String switchDpid);
+
+ @Property("in_port")
+ public Short getInPort();
+
+ @Property("in_port")
+ public void setInPort(Short inPort);
+
+ @Property("out_port")
+ public Short getOutPort();
+
+ @Property("out_port")
+ public void setOutPort(Short outPort);
+
+ @Property("user_state")
+ public String getUserState();
+
+ @Property("user_state")
+ public void setUserState(String userState);
+
+ @Property("switch_state")
+ public String getSwitchState();
+
+ @Property("switch_state")
+ public void setSwitchState(String switchState);
+
+ @Property("error_state_type")
+ public String getErrorStateType();
+
+ @Property("error_state_type")
+ public void setErrorStateType(String errorStateType);
+
+ @Property("error_state_code")
+ public String getErrorStateCode();
+
+ @Property("error_state_code")
+ public void setErrorStateCode(String errorStateCode);
+ }
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 12ed505..11519b7 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -1,10 +1,22 @@
package net.floodlightcontroller.flowcache;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.EnumSet;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.INetMapStorage;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -13,20 +25,178 @@
import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.util.CallerId;
+import net.floodlightcontroller.util.DataPath;
+import net.floodlightcontroller.util.Dpid;
import net.floodlightcontroller.util.DataPathEndpoints;
+import net.floodlightcontroller.util.FlowEntry;
+import net.floodlightcontroller.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowEntrySwitchState;
+import net.floodlightcontroller.util.FlowEntryUserState;
import net.floodlightcontroller.util.FlowId;
import net.floodlightcontroller.util.FlowPath;
+import net.floodlightcontroller.util.OFMessageDamper;
+import net.floodlightcontroller.util.Port;
+import net.onrc.onos.util.GraphDBConnection;
+import net.onrc.onos.util.GraphDBConnection.Transaction;
+
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class FlowManager implements IFloodlightModule, IFlowService {
+public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
+
+ public GraphDBConnection conn;
protected IRestApiService restApi;
+ protected IFloodlightProviderService floodlightProvider;
+
+ protected OFMessageDamper messageDamper;
+
+ protected static int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
+ protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
+ public static short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0; // infinity
+ public static short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
/** The logger. */
- private static Logger logger =
- LoggerFactory.getLogger(FlowManager.class);
+ private static Logger log = LoggerFactory.getLogger(FlowManager.class);
+
+ // The periodic task(s)
+ private final ScheduledExecutorService scheduler =
+ Executors.newScheduledThreadPool(1);
+ final Runnable reader = new Runnable() {
+ public void run() {
+ // log.debug("Reading Flow Entries from the Network Map...");
+ if (floodlightProvider == null) {
+ log.debug("FloodlightProvider service not found!");
+ return;
+ }
+
+ Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+
+ // Fetch all Flow Entries
+ Iterable<IFlowEntry> flowEntries = conn.utils().getAllFlowEntries(conn);
+ for (IFlowEntry flowEntryObj : flowEntries) {
+ FlowEntryId flowEntryId =
+ new FlowEntryId(flowEntryObj.getFlowEntryId());
+ String userState = flowEntryObj.getUserState();
+ String switchState = flowEntryObj.getSwitchState();
+
+ log.debug("Found Flow Entry {}: ", flowEntryId.toString());
+ log.debug("User State {}:", userState);
+ log.debug("Switch State {}:", switchState);
+
+ if (! switchState.equals("FE_SWITCH_NOT_UPDATED")) {
+ // Ignore the entry: nothing to do
+ continue;
+ }
+
+ Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
+ IOFSwitch mySwitch = mySwitches.get(dpid.value());
+ if (mySwitch == null) {
+ log.debug("Flow Entry ignored: not my switch");
+ continue;
+ }
+
+ //
+ // Create the Open Flow Flow Modification Entry to push
+ //
+ OFFlowMod fm =
+ (OFFlowMod) floodlightProvider.getOFMessageFactory()
+ .getMessage(OFType.FLOW_MOD);
+ long cookie = flowEntryId.value();
+
+ short flowModCommand = OFFlowMod.OFPFC_ADD;
+ if (userState.equals("FE_USER_ADD")) {
+ flowModCommand = OFFlowMod.OFPFC_ADD;
+ } else if (userState.equals("FE_USER_MODIFY")) {
+ flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
+ } else if (userState.equals("FE_USER_DELETE")) {
+ flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
+ } else {
+ // Unknown user state. Ignore the entry
+ continue;
+ }
+
+ OFMatch match = new OFMatch();
+ match.setInputPort(flowEntryObj.getInPort());
+
+ OFActionOutput action = new OFActionOutput();
+ action.setMaxLength((short)0xffff);
+ action.setPort(flowEntryObj.getOutPort());
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(action);
+
+ fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+ .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+ .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()
+ //
+ try {
+ messageDamper.write(mySwitch, fm, null);
+ mySwitch.flush();
+ flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
+ if (userState.equals("FE_USER_DELETE")) {
+ // Delete the entry
+ IFlowPath flowObj = null;
+ flowObj = conn.utils().getFlowPathByFlowEntry(conn,
+ flowEntryObj);
+ if (flowObj != null)
+ log.debug("Found FlowPath to be deleted");
+ else
+ log.debug("Did not find FlowPath to be deleted");
+ flowObj.removeFlowEntry(flowEntryObj);
+ conn.utils().removeFlowEntry(conn, flowEntryObj);
+
+ // Test whether the last flow entry
+ Iterable<IFlowEntry> tmpflowEntries =
+ flowObj.getFlowEntries();
+ boolean found = false;
+ for (IFlowEntry tmpflowEntryObj : tmpflowEntries) {
+ found = true;
+ break;
+ }
+ if (! found) {
+ // Remove the Flow Path as well
+ conn.utils().removeFlowPath(conn, flowObj);
+ }
+ }
+ } catch (IOException e) {
+ log.error("Failure writing flow mod from network map", e);
+ }
+ }
+ conn.endTx(Transaction.COMMIT);
+ }
+ };
+ final ScheduledFuture<?> readerHandle =
+ scheduler.scheduleAtFixedRate(reader, 3, 3, TimeUnit.SECONDS);
+
+ @Override
+ public void init(String conf) {
+ conn = GraphDBConnection.getInstance(conf);
+ }
+
+ public void finalize() {
+ close();
+ }
+
+ @Override
+ public void close() {
+ conn.close();
+ }
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -52,6 +222,7 @@
getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IFloodlightProviderService.class);
l.add(IRestApiService.class);
return l;
}
@@ -59,7 +230,14 @@
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
restApi = context.getServiceImpl(IRestApiService.class);
+ messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+ EnumSet.of(OFType.FLOW_MOD),
+ OFMESSAGE_DAMPER_TIMEOUT);
+ // TODO: An ugly hack!
+ String conf = "/tmp/cassandra.titan";
+ this.init(conf);
}
@Override
@@ -79,7 +257,135 @@
*/
@Override
public boolean addFlow(FlowPath flowPath, FlowId flowId) {
- // TODO
+
+ //
+ // Assign the FlowEntry IDs
+ // TODO: This is an ugly hack!
+ // The Flow Entry IDs are set to 1000*FlowId + Index
+ //
+ int i = 1;
+ for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+ long id = flowPath.flowId().value() * 1000 + i;
+ ++i;
+ flowEntry.setFlowEntryId(new FlowEntryId(id));
+ }
+
+ IFlowPath flowObj = null;
+ try {
+ if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
+ != null) {
+ log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
+ flowPath.flowId().toString());
+ } else {
+ flowObj = conn.utils().newFlowPath(conn);
+ log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
+ flowPath.flowId().toString());
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":addFlow FlowId:{} failed",
+ flowPath.flowId().toString());
+ }
+ if (flowObj == null)
+ return false;
+
+ //
+ // Set the Flow key:
+ // - flowId
+ //
+ flowObj.setFlowId(flowPath.flowId().toString());
+ flowObj.setType("flow");
+
+ //
+ // Set the Flow attributes:
+ // - flowPath.installerId()
+ // - flowPath.dataPath().srcPort()
+ // - flowPath.dataPath().dstPort()
+ //
+ flowObj.setInstallerId(flowPath.installerId().toString());
+ flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
+ flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
+ flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
+ flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
+
+ // Flow edges:
+ // HeadFE
+
+
+ //
+ // Flow Entries:
+ // flowPath.dataPath().flowEntries()
+ //
+ for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+ IFlowEntry flowEntryObj = null;
+ boolean found = false;
+ try {
+ if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
+ log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
+ flowEntry.flowEntryId().toString());
+ found = true;
+ } else {
+ flowEntryObj = conn.utils().newFlowEntry(conn);
+ log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
+ flowEntry.flowEntryId().toString());
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":addFlow FlowEntryId:{} failed",
+ flowEntry.flowEntryId().toString());
+ }
+ if (flowEntryObj == null)
+ return false;
+
+ //
+ // Set the Flow Entry key:
+ // - flowEntry.flowEntryId()
+ //
+ flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
+ flowEntryObj.setType("flow_entry");
+
+ //
+ // Set the Flow Entry attributes:
+ // - flowEntry.flowEntryMatch()
+ // - flowEntry.flowEntryActions()
+ // - flowEntry.dpid()
+ // - flowEntry.inPort()
+ // - flowEntry.outPort()
+ // - flowEntry.flowEntryUserState()
+ // - flowEntry.flowEntrySwitchState()
+ // - flowEntry.flowEntryErrorState()
+ //
+ flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
+ flowEntryObj.setInPort(flowEntry.inPort().value());
+ flowEntryObj.setOutPort(flowEntry.outPort().value());
+ // TODO: Hacks with hard-coded state names!
+ if (found)
+ flowEntryObj.setUserState("FE_USER_MODIFY");
+ else
+ flowEntryObj.setUserState("FE_USER_ADD");
+ flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
+ //
+ // TODO: Take care of the FlowEntryMatch, FlowEntryActions,
+ // and FlowEntryErrorState.
+ //
+
+ // Flow Entries edges:
+ // Flow
+ // NextFE
+ // InPort
+ // OutPort
+ // Switch
+ if (! found)
+ flowObj.addFlowEntry(flowEntryObj);
+ }
+ conn.endTx(Transaction.COMMIT);
+
+ //
+ // TODO: We need a proper Flow ID allocation mechanism.
+ //
+ flowId.setValue(flowPath.flowId().value());
return true;
}
@@ -91,7 +397,46 @@
*/
@Override
public boolean deleteFlow(FlowId flowId) {
- // TODO
+ IFlowPath flowObj = null;
+ //
+ // We just mark the entries for deletion,
+ // and let the switches remove each individual entry after
+ // it has been removed from the switches.
+ //
+ try {
+ if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
+ != null) {
+ log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
+ flowId.toString());
+ } else {
+ log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
+ flowId.toString());
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":deleteFlow FlowId:{} failed", flowId.toString());
+ }
+ if (flowObj == null)
+ return true; // OK: No such flow
+
+ //
+ // Find and mark for deletion all Flow Entries
+ //
+ Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+ boolean empty = true; // TODO: an ugly hack
+ for (IFlowEntry flowEntryObj : flowEntries) {
+ empty = false;
+ // flowObj.removeFlowEntry(flowEntryObj);
+ // conn.utils().removeFlowEntry(conn, flowEntryObj);
+ flowEntryObj.setUserState("FE_USER_DELETE");
+ flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
+ }
+ // Remove from the database empty flows
+ if (empty)
+ conn.utils().removeFlowPath(conn, flowObj);
+ conn.endTx(Transaction.COMMIT);
+
return true;
}
@@ -99,13 +444,62 @@
* Get a previously added flow.
*
* @param flowId the Flow ID of the flow to get.
- * @param flowPath the return-by-reference flow path.
- * @return true on success, otherwise false.
+ * @return the Flow Path if found, otherwise null.
*/
@Override
- public boolean getFlow(FlowId flowId, FlowPath flowPath) {
- // TODO
- return true;
+ public FlowPath getFlow(FlowId flowId) {
+ IFlowPath flowObj = null;
+ try {
+ if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
+ != null) {
+ log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
+ flowId.toString());
+ } else {
+ log.debug("Get FlowPath with FlowId {}: FlowPath not found",
+ flowId.toString());
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":getFlow FlowId:{} failed", flowId.toString());
+ }
+ if (flowObj == null)
+ return null; // Flow not found
+
+ //
+ // Extract the Flow state
+ //
+ FlowPath flowPath = new FlowPath();
+ 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()));
+
+ //
+ // Extract all Flow Entries
+ //
+ 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()));
+ flowEntry.setInPort(new Port(flowEntryObj.getInPort()));
+ flowEntry.setOutPort(new Port(flowEntryObj.getOutPort()));
+ String userState = flowEntryObj.getUserState();
+ flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
+ String switchState = flowEntryObj.getSwitchState();
+ flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
+ //
+ // TODO: Take care of the FlowEntryMatch, FlowEntryActions,
+ // and FlowEntryErrorState.
+ //
+ flowPath.dataPath().flowEntries().add(flowEntry);
+ }
+ conn.endTx(Transaction.COMMIT);
+
+ return flowPath;
}
/**
@@ -114,36 +508,35 @@
*
* @param installerId the Caller ID of the installer of the flow to get.
* @param dataPathEndpoints the data path endpoints of the flow to get.
- * @param flowPath the return-by-reference flow path.
- * @return true on success, otherwise false.
+ * @return the Flow Path if found, otherwise null.
*/
@Override
- public boolean getFlow(CallerId installerId,
- DataPathEndpoints dataPathEndpoints,
- FlowPath flowPath) {
+ public FlowPath getFlow(CallerId installerId,
+ DataPathEndpoints dataPathEndpoints) {
// TODO
- return true;
+ return null;
}
/**
* Get all installed flows by all installers for given data path endpoints.
*
* @param dataPathEndpoints the data path endpoints of the flows to get.
- * @param flowPaths the return-by-reference list of flows.
+ * @return the Flow Paths if found, otherwise null.
*/
@Override
- public void getAllFlows(DataPathEndpoints dataPathEndpoints,
- ArrayList<FlowPath> flowPaths) {
+ public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
// TODO
+ return null;
}
/**
* Get all installed flows by all installers.
*
- * @param flowPaths the return-by-reference list of flows.
+ * @return the Flow Paths if found, otherwise null.
*/
@Override
- public void getAllFlows(ArrayList<FlowPath> flowPaths) {
+ public ArrayList<FlowPath> getAllFlows() {
// TODO
+ return null;
}
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 956caab..c14c0c1 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -36,10 +36,9 @@
* Get a previously added flow.
*
* @param flowId the Flow ID of the flow to get.
- * @param flowPath the return-by-reference flow path.
- * @return true on success, otherwise false.
+ * @return the Flow Path if found, otherwise null.
*/
- boolean getFlow(FlowId flowId, FlowPath flowPath);
+ FlowPath getFlow(FlowId flowId);
/**
* Get a previously added flow by a specific installer for given
@@ -47,26 +46,23 @@
*
* @param installerId the Caller ID of the installer of the flow to get.
* @param dataPathEndpoints the data path endpoints of the flow to get.
- * @param flowPath the return-by-reference flow path.
- * @return true on success, otherwise false.
+ * @return the Flow Path if found, otherwise null.
*/
- boolean getFlow(CallerId installerId,
- DataPathEndpoints dataPathEndpoints,
- FlowPath flowPath);
+ FlowPath getFlow(CallerId installerId,
+ DataPathEndpoints dataPathEndpoints);
/**
* Get all installed flows by all installers for given data path endpoints.
*
* @param dataPathEndpoints the data path endpoints of the flows to get.
- * @param flowPaths the return-by-reference list of flows.
+ * @return the Flow Paths if found, otherwise null.
*/
- void getAllFlows(DataPathEndpoints dataPathEndpoints,
- ArrayList<FlowPath> flowPaths);
+ ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints);
/**
* Get all installed flows by all installers.
*
- * @param flowPaths the return-by-reference list of flows.
+ * @return the Flow Paths if found, otherwise null.
*/
- void getAllFlows(ArrayList<FlowPath> flowPaths);
+ ArrayList<FlowPath> getAllFlows();
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
index feb43d3..1646012 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
@@ -38,6 +38,9 @@
//
ObjectMapper mapper = new ObjectMapper();
String flowPathStr = (String) getRequestAttributes().get("flow");
+ // TODO: Remove it later
+ // String flowPathStr = "{\"flowId\":{\"value\":\"5\"},\"installerId\":{\"value\":\"FOOBAR\"},\"dataPath\":{\"srcPort\":{\"dpid\":{\"value\":\"00:00:00:00:00:00:00:01\"},\"port\":{\"value\":0}},\"dstPort\":{\"dpid\":{\"value\":\"00:00:00:00:00:00:00:02\"},\"port\":{\"value\":0}},\"flowEntries\":[{\"flowEntryId\":null,\"flowEntryMatch\":null,\"flowEntryActions\":null,\"dpid\":{\"value\":\"00:00:00:00:00:00:00:01\"},\"inPort\":{\"value\":0},\"outPort\":{\"value\":1},\"flowEntryUserState\":\"FE_USER_UNKNOWN\",\"flowEntrySwitchState\":\"FE_SWITCH_UNKNOWN\",\"flowEntryErrorState\":null},{\"flowEntryId\":null,\"flowEntryMatch\":null,\"flowEntryActions\":null,\"dpid\":{\"value\":\"00:00:00:00:00:00:00:02\"},\"inPort\":{\"value\":9},\"outPort\":{\"value\":0},\"flowEntryUserState\":\"FE_USER_UNKNOWN\",\"flowEntrySwitchState\":\"FE_SWITCH_UNKNOWN\",\"flowEntryErrorState\":null}]}}";
+
FlowPath flowPath = null;
log.debug("Add Flow Path: " + flowPathStr);
try {
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
index c485d91..34d79c8 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
@@ -19,7 +19,7 @@
@Get("json")
public ArrayList<FlowPath> retrieve() {
- ArrayList<FlowPath> result = new ArrayList<FlowPath>();
+ ArrayList<FlowPath> result = null;
IFlowService flowService =
(IFlowService)getContext().getAttributes().
@@ -48,7 +48,7 @@
DataPathEndpoints dataPathEndpoints =
new DataPathEndpoints(srcSwitchPort, dstSwitchPort);
- flowService.getAllFlows(dataPathEndpoints, result);
+ result = flowService.getAllFlows(dataPathEndpoints);
return result;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
index deb4d04..92317cf 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
@@ -15,7 +15,7 @@
@Get("json")
public ArrayList<FlowPath> retrieve() {
- ArrayList<FlowPath> result = new ArrayList<FlowPath>();
+ ArrayList<FlowPath> result = null;
IFlowService flowService =
(IFlowService)getContext().getAttributes().
@@ -29,9 +29,7 @@
// Extract the arguments
log.debug("Get All Flows Endpoints");
- flowService.getAllFlows(result);
- FlowPath flowPath = new FlowPath();
- result.add(flowPath);
+ result = flowService.getAllFlows();
return result;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
index d5b2730..85d5b7e 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
@@ -31,7 +31,7 @@
log.debug("Get Flow Id: " + flowIdStr);
- flowService.getFlow(flowId, result);
+ result = flowService.getFlow(flowId);
return result;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java
index cb4e6ef..33eca0b 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java
@@ -50,7 +50,7 @@
DataPathEndpoints dataPathEndpoints =
new DataPathEndpoints(srcSwitchPort, dstSwitchPort);
- flowService.getFlow(installerId, dataPathEndpoints, result);
+ result = flowService.getFlow(installerId, dataPathEndpoints);
return result;
}
diff --git a/src/main/java/net/floodlightcontroller/util/DataPath.java b/src/main/java/net/floodlightcontroller/util/DataPath.java
index 71e0a2f..b2dded6 100644
--- a/src/main/java/net/floodlightcontroller/util/DataPath.java
+++ b/src/main/java/net/floodlightcontroller/util/DataPath.java
@@ -19,6 +19,8 @@
* Default constructor.
*/
public DataPath() {
+ srcPort = new SwitchPort();
+ dstPort = new SwitchPort();
flowEntries = new ArrayList<FlowEntry>();
}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntry.java b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
index dfb8f82..efab0cf 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntry.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
@@ -4,33 +4,13 @@
import net.floodlightcontroller.util.FlowEntryActions;
import net.floodlightcontroller.util.FlowEntryId;
import net.floodlightcontroller.util.FlowEntryMatch;
+import net.floodlightcontroller.util.FlowEntrySwitchState;
+import net.floodlightcontroller.util.FlowEntryUserState;
import net.floodlightcontroller.util.Port;
import org.codehaus.jackson.annotate.JsonProperty;
/**
- * The Flow Entry state as set by the user (via the ONOS API).
- */
-enum FlowEntryUserState {
- FE_USER_UNKNOWN, // Initialization value: state unknown
- FE_USER_ADD, // Flow entry that is added
- FE_USER_MODIFY, // Flow entry that is modified
- FE_USER_DELETE // Flow entry that is deleted
-}
-
-/**
- * The Flow Entry state as set by the controller.
- */
-enum FlowEntrySwitchState {
- FE_SWITCH_UNKNOWN, // Initialization value: state unknown
- FE_SWITCH_NOT_UPDATED, // Switch not updated with this entry
- FE_SWITCH_UPDATE_IN_PROGRESS, // Switch update in progress
- FE_SWITCH_UPDATED, // Switch updated with this entry
- FE_SWITCH_UPDATE_FAILED // Error updating the switch with this entry
-}
-
-
-/**
* The class representing the Flow Entry.
*
* NOTE: The specification is incomplete. E.g., the entry needs to
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryId.java b/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
index 0874bdb..d322f5e 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
@@ -63,6 +63,6 @@
*/
@Override
public String toString() {
- return Long.toHexString(this.value);
+ return "0x" + Long.toHexString(this.value);
}
}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java b/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java
new file mode 100644
index 0000000..4f9882a
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java
@@ -0,0 +1,12 @@
+package net.floodlightcontroller.util;
+
+/**
+ * The Flow Entry state as set by the controller.
+ */
+public enum FlowEntrySwitchState {
+ FE_SWITCH_UNKNOWN, // Initialization value: state unknown
+ FE_SWITCH_NOT_UPDATED, // Switch not updated with this entry
+ FE_SWITCH_UPDATE_IN_PROGRESS, // Switch update in progress
+ FE_SWITCH_UPDATED, // Switch updated with this entry
+ FE_SWITCH_UPDATE_FAILED // Error updating the switch with this entry
+}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java b/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java
new file mode 100644
index 0000000..8637b4f
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java
@@ -0,0 +1,11 @@
+package net.floodlightcontroller.util;
+
+/**
+ * The Flow Entry state as set by the user (via the ONOS API).
+ */
+public enum FlowEntryUserState {
+ FE_USER_UNKNOWN, // Initialization value: state unknown
+ FE_USER_ADD, // Flow entry that is added
+ FE_USER_MODIFY, // Flow entry that is modified
+ FE_USER_DELETE // Flow entry that is deleted
+}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowPath.java b/src/main/java/net/floodlightcontroller/util/FlowPath.java
index 5b3bbd1..11f23fe 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowPath.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowPath.java
@@ -18,6 +18,7 @@
* Default constructor.
*/
public FlowPath() {
+ dataPath = new DataPath();
}
/**
diff --git a/src/main/java/net/onrc/onos/util/GraphDBConnection.java b/src/main/java/net/onrc/onos/util/GraphDBConnection.java
index a2a8689..724095b 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBConnection.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBConnection.java
@@ -37,6 +37,13 @@
if (!s.contains("dl_address")) {
graph.createKeyIndex("dl_address", Vertex.class);
}
+ if (!s.contains("flow_id")) {
+ graph.createKeyIndex("flow_id", Vertex.class);
+ }
+ if (!s.contains("flow_entry_id")) {
+ graph.createKeyIndex("flow_entry_id",
+ Vertex.class);
+ }
}
graph.stopTransaction(Conclusion.SUCCESS);
if (utils == null) {
diff --git a/src/main/java/net/onrc/onos/util/GraphDBUtils.java b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
index d01de21..ebc5942 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
@@ -7,8 +7,12 @@
import com.tinkerpop.gremlin.java.GremlinPipeline;
import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
+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.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowId;
public class GraphDBUtils implements IDBUtils {
@@ -60,4 +64,69 @@
return fg.getVertices("type","device",IDeviceObject.class);
}
+ @Override
+ public IFlowPath searchFlowPath(GraphDBConnection conn,
+ FlowId flowId) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+
+ return fg.getVertices("flow_id", flowId.toString()).iterator().hasNext() ?
+ fg.getVertices("flow_id", flowId.toString(),
+ IFlowPath.class).iterator().next() : null;
+ }
+
+ @Override
+ public IFlowPath newFlowPath(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ IFlowPath flowPath = fg.addVertex(null, IFlowPath.class);
+ return flowPath;
+ }
+
+ @Override
+ public void removeFlowPath(GraphDBConnection conn,
+ IFlowPath flowPath) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ fg.removeVertex(flowPath.asVertex());
+ }
+
+ @Override
+ public IFlowPath getFlowPathByFlowEntry(GraphDBConnection conn,
+ IFlowEntry flowEntry) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ GremlinPipeline<Vertex, IFlowPath> pipe = new GremlinPipeline<Vertex, IFlowPath>();
+ pipe.start(flowEntry.asVertex());
+ pipe.out("flow");
+ FramedVertexIterable<IFlowPath> r = new FramedVertexIterable(conn.getFramedGraph(), pipe, IFlowPath.class);
+ return r.iterator().hasNext() ? r.iterator().next() : null;
+ }
+
+ @Override
+ public IFlowEntry searchFlowEntry(GraphDBConnection conn,
+ FlowEntryId flowEntryId) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+
+ return fg.getVertices("flow_entry_id", flowEntryId.toString()).iterator().hasNext() ?
+ fg.getVertices("flow_entry_id", flowEntryId.toString(),
+ IFlowEntry.class).iterator().next() : null;
+ }
+
+ @Override
+ public IFlowEntry newFlowEntry(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ IFlowEntry flowEntry = fg.addVertex(null, IFlowEntry.class);
+ return flowEntry;
+ }
+
+ @Override
+ public void removeFlowEntry(GraphDBConnection conn,
+ IFlowEntry flowEntry) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ fg.removeVertex(flowEntry.asVertex());
+ }
+
+ @Override
+ public Iterable<IFlowEntry> getAllFlowEntries(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+
+ return fg.getVertices("type", "flow_entry", IFlowEntry.class);
+ }
}
diff --git a/src/main/java/net/onrc/onos/util/IDBUtils.java b/src/main/java/net/onrc/onos/util/IDBUtils.java
index a27a261..4ff70f3 100644
--- a/src/main/java/net/onrc/onos/util/IDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/IDBUtils.java
@@ -1,8 +1,12 @@
package net.onrc.onos.util;
import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
+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.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowId;
public interface IDBUtils {
public ISwitchObject searchSwitch(GraphDBConnection conn, String dpid);
@@ -11,4 +15,15 @@
public void removeDevice(GraphDBConnection conn, IDeviceObject dev);
public IPortObject searchPort(GraphDBConnection conn, String dpid, short number);
public Iterable<IDeviceObject> getDevices(GraphDBConnection conn);
+ public IFlowPath searchFlowPath(GraphDBConnection conn, FlowId flowId);
+ public IFlowPath newFlowPath(GraphDBConnection conn);
+ public void removeFlowPath(GraphDBConnection conn, IFlowPath flowPath);
+ public IFlowPath getFlowPathByFlowEntry(GraphDBConnection conn,
+ IFlowEntry flowEntry);
+ public IFlowEntry searchFlowEntry(GraphDBConnection conn,
+ FlowEntryId flowEntryId);
+ public IFlowEntry newFlowEntry(GraphDBConnection conn);
+ public void removeFlowEntry(GraphDBConnection conn,
+ IFlowEntry flowEntry);
+ public Iterable<IFlowEntry> getAllFlowEntries(GraphDBConnection conn);
}