Fixed "Intent" problem: Add flow removed event by using "DEL_ACK".
Fixed "Fowarding" to make it work on changed "Intent".
Did cleanup some of "Intent" measurement code.
Deleted unused receiver in FlowProgrammer.java.
Memo:
ReactiveForwarding can set idle timeout.
You can specify it from 1 ~ second.
Out of this range, the application set it to 5 sec (default).
Sample: (in onos.properties file)
net.onrc.onos.apps.forwarding.Forwarding.idletimeout = 5
When you test, please be careful we only set the value to the first switch and the other switches has +2 second idle timeout values.
Change-Id: I1b261a8c771356a412af004c7c0cd93b539ebba7
diff --git a/src/main/java/net/onrc/onos/apps/forwarding/Forwarding.java b/src/main/java/net/onrc/onos/apps/forwarding/Forwarding.java
index 9fd8870..e59b33c 100644
--- a/src/main/java/net/onrc/onos/apps/forwarding/Forwarding.java
+++ b/src/main/java/net/onrc/onos/apps/forwarding/Forwarding.java
@@ -68,7 +68,8 @@
private ITopologyService topologyService;
private Topology topology;
private IPathCalcRuntimeService pathRuntime;
- private IntentMap intentMap;
+ private IntentMap pathIntentMap;
+ private IntentMap highLevelIntentMap;
// TODO it seems there is a Guava collection that will time out entries.
// We should see if this will work here.
@@ -191,8 +192,9 @@
packetService.registerPacketListener(this);
topology = topologyService.getTopology();
- intentMap = pathRuntime.getPathIntents();
- intentMap.addChangeListener(this);
+ highLevelIntentMap = pathRuntime.getHighLevelIntents();
+ pathIntentMap = pathRuntime.getPathIntents();
+ pathIntentMap.addChangeListener(this);
}
@Override
@@ -273,7 +275,7 @@
private void continueHandlePacketIn(Switch sw, Port inPort, Ethernet eth, Device deviceObject) {
- log.debug("Start continuehandlePacketIn");
+ log.trace("Start continuehandlePacketIn");
//Iterator<IPortObject> ports = deviceObject.getAttachedPorts().iterator();
Iterator<net.onrc.onos.core.topology.Port> ports = deviceObject.getAttachmentPoints().iterator();
@@ -300,10 +302,12 @@
MACAddress srcMacAddress = MACAddress.valueOf(eth.getSourceMACAddress());
MACAddress dstMacAddress = MACAddress.valueOf(eth.getDestinationMACAddress());
+ Path pathspec = new Path(srcMacAddress, dstMacAddress);
+ IntentOperationList operations = new IntentOperationList();
synchronized (lock) {
//TODO check concurrency
- Path pathspec = new Path(srcMacAddress, dstMacAddress);
+
PushedFlow existingFlow = pendingFlows.get(pathspec);
//A path is installed side by side to reduce a path timeout and a wrong state.
@@ -323,23 +327,58 @@
// Flow has been sent to the switches so it is safe to
// send a packet out now
- Intent intent = intentMap.getIntent(existingFlow.intentId);
- PathIntent pathIntent = null;
- if (intent instanceof PathIntent) {
- pathIntent = (PathIntent) intent;
+ // TODO Here highLevelIntentMap and pathIntentMap would be problem,
+ // because it doesn't have global information as of May 2014.
+ // However usually these lines here is used when we got packet-in and this class think
+ // the path for the packet is installed already, so it is pretty rare.
+ // I will leave it for now, and will work in the next step.
+ Intent highLevelIntent = highLevelIntentMap.getIntent(existingFlow.intentId);
+ if (highLevelIntent == null) {
+ log.debug("Intent ID {} is null in HighLevelIntentMap. return.", existingFlow.intentId);
+ return;
+ }
+
+ if (highLevelIntent.getState() != IntentState.INST_ACK) {
+ log.debug("Intent ID {}'s state is not INST_ACK. return.", existingFlow.intentId);
+ return;
+ }
+
+ ShortestPathIntent spfIntent = null;
+ if (highLevelIntent instanceof ShortestPathIntent) {
+ spfIntent = (ShortestPathIntent) highLevelIntent;
} else {
log.debug("Intent ID {} is not PathIntent or null. return.", existingFlow.intentId);
return;
}
- Boolean isflowEntryForThisSwitch = false;
+ PathIntent pathIntent = (PathIntent) pathIntentMap.getIntent(spfIntent.getPathIntentId());
+ if (pathIntent == null) {
+ log.debug("PathIntent ID {} is null in PathIntentMap. return.", existingFlow.intentId);
+ return;
+ }
+
+ if (pathIntent.getState() != IntentState.INST_ACK) {
+ log.debug("Intent ID {}'s state is not INST_ACK. return.", existingFlow.intentId);
+ return;
+ }
+
+ boolean isflowEntryForThisSwitch = false;
net.onrc.onos.core.intent.Path path = pathIntent.getPath();
+ long outPort = -1;
+
+ if (spfIntent.getDstSwitchDpid() == sw.getDpid()) {
+ log.trace("The packet-in sw dpid {} is on the path.", sw.getDpid());
+ isflowEntryForThisSwitch = true;
+ outPort = spfIntent.getDstPortNumber();
+ }
for (Iterator<LinkEvent> i = path.iterator(); i.hasNext();) {
LinkEvent le = i.next();
+
if (le.getSrc().dpid.equals(sw.getDpid())) {
- log.debug("src {} dst {}", le.getSrc(), le.getDst());
+ log.trace("The packet-in sw dpid {} is on the path.", sw.getDpid());
isflowEntryForThisSwitch = true;
+ outPort = le.getSrc().getNumber();
break;
}
}
@@ -352,25 +391,24 @@
existingFlow.intentId,
srcMacAddress, dstMacAddress);
} else {
- log.debug("Sending packet out from sw {}, outport{}", sw, existingFlow.firstOutPort);
+ if (outPort < 0) {
+ outPort = existingFlow.firstOutPort;
+ }
+ log.debug("Sending packet out from sw {}, outport{}", sw.getDpid(), outPort);
packetService.sendPacket(eth, new SwitchPort(
- sw.getDpid(), existingFlow.firstOutPort));
+ sw.getDpid(), (short) outPort));
}
} else {
// Flow path has not yet been installed to switches so save the
// packet out for later
- log.debug("Put a packet into the waiting list. flowId {}", existingFlow.intentId);
+ log.trace("Put a packet into the waiting list. flowId {}", existingFlow.intentId);
waitingPackets.put(existingFlow.intentId, new PacketToPush(eth, sw.getDpid()));
}
return;
}
- log.debug("Adding new flow between {} at {} and {} at {}",
- new Object[]{srcMacAddress, srcSwitchPort, dstMacAddress, dstSwitchPort});
-
- String intentId = callerId + ":" + controllerRegistryService.getNextUniqueId();
- IntentOperationList operations = new IntentOperationList();
+ String intentId = Long.toString(controllerRegistryService.getNextUniqueId());
ShortestPathIntent intent = new ShortestPathIntent(intentId,
sw.getDpid(), inPort.getNumber(), srcMacAddress.toLong(),
destinationDpid, destinationPort, dstMacAddress.toLong());
@@ -379,14 +417,16 @@
intent.setFirstSwitchIdleTimeout(idleTimeout);
IntentOperation.Operator operator = IntentOperation.Operator.ADD;
operations.add(operator, intent);
- pathRuntime.executeIntentOperations(operations);
- // Add to waiting lists
- pendingFlows.put(pathspec, new PushedFlow(intentId));
- log.debug("Put a Path {} in the pending flow, intent ID {}", pathspec, intentId);
- waitingPackets.put(intentId, new PacketToPush(eth, sw.getDpid()));
- log.debug("Put a Packet in the wating list. related pathspec {}", pathspec);
+ log.debug("Adding new flow between {} at {} and {} at {}",
+ new Object[]{srcMacAddress, srcSwitchPort, dstMacAddress, dstSwitchPort});
+ // Add to waiting lists
+ waitingPackets.put(intentId, new PacketToPush(eth, sw.getDpid()));
+ log.trace("Put a Packet in the wating list. intent ID {}, related pathspec {}", intentId, pathspec);
+ pendingFlows.put(pathspec, new PushedFlow(intentId));
+ log.trace("Put a Path {} in the pending flow, intent ID {}", pathspec, intentId);
}
+ pathRuntime.executeIntentOperations(operations);
}
@Override
@@ -406,7 +446,6 @@
MACAddress srcMacAddress = MACAddress.valueOf(spfIntent.getSrcMac());
MACAddress dstMacAddress = MACAddress.valueOf(spfIntent.getDstMac());
Path removedPath = new Path(srcMacAddress, dstMacAddress);
-
synchronized (lock) {
// There *shouldn't* be any packets queued if the flow has
// just been removed.
@@ -414,6 +453,7 @@
if (!packets.isEmpty()) {
log.warn("Removed flow {} has packets queued.", spfIntent.getId());
}
+
pendingFlows.remove(removedPath);
log.debug("Removed from the pendingFlow: Path {}, Flow ID {}", removedPath, spfIntent.getId());
}
@@ -484,7 +524,7 @@
public void intentsChange(LinkedList<ChangedEvent> events) {
for (ChangedEvent event : events) {
log.debug("path intent ID {}, eventType {}", event.intent.getId() , event.eventType);
- PathIntent pathIntent = (PathIntent) intentMap.getIntent(event.intent.getId());
+ PathIntent pathIntent = (PathIntent) pathIntentMap.getIntent(event.intent.getId());
if (pathIntent == null) {
continue;
}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/FlowProgrammer.java b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowProgrammer.java
index 4f6451e..8a3739d 100644
--- a/src/main/java/net/onrc/onos/core/flowprogrammer/FlowProgrammer.java
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowProgrammer.java
@@ -5,9 +5,7 @@
import java.util.HashMap;
import java.util.Map;
-import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
-import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
@@ -17,11 +15,7 @@
import net.floodlightcontroller.restserver.IRestApiService;
import net.onrc.onos.core.flowprogrammer.web.FlowProgrammerWebRoutable;
import net.onrc.onos.core.registry.IControllerRegistryService;
-import net.onrc.onos.core.util.FlowEntryId;
-import org.openflow.protocol.OFFlowRemoved;
-import org.openflow.protocol.OFMessage;
-import org.openflow.protocol.OFType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,7 +33,6 @@
* @author Brian
*/
public class FlowProgrammer implements IFloodlightModule,
- IOFMessageListener,
IOFSwitchListener {
// flag to enable FlowSynchronizer
private static final boolean ENABLE_FLOW_SYNC = false;
@@ -76,7 +69,6 @@
public void startUp(FloodlightModuleContext context) {
restApi.addRestletRoutable(new FlowProgrammerWebRoutable());
pusher.start();
- floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
floodlightProvider.addOFSwitchListener(this);
}
@@ -120,31 +112,6 @@
}
@Override
- public boolean isCallbackOrderingPrereq(OFType type, String name) {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public boolean isCallbackOrderingPostreq(OFType type, String name) {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
- if (msg.getType().equals(OFType.FLOW_REMOVED) &&
- (msg instanceof OFFlowRemoved)) {
- OFFlowRemoved flowMsg = (OFFlowRemoved) msg;
- FlowEntryId id = new FlowEntryId(flowMsg.getCookie());
- log.debug("Got flow entry removed from {}: {}", sw.getId(), id);
- // TODO: Inform the Forwarding module that a flow has expired
- }
-
- return Command.CONTINUE;
- }
-
- @Override
public void addedSwitch(IOFSwitch sw) {
log.debug("Switch added: {}", sw.getId());
diff --git a/src/main/java/net/onrc/onos/core/intent/FlowEntry.java b/src/main/java/net/onrc/onos/core/intent/FlowEntry.java
index bb051e2..94cf88a 100644
--- a/src/main/java/net/onrc/onos/core/intent/FlowEntry.java
+++ b/src/main/java/net/onrc/onos/core/intent/FlowEntry.java
@@ -21,6 +21,7 @@
protected Operator operator;
protected int hardTimeout = 0;
protected int idleTimeout = 0;
+ protected long flowEntryId;
public FlowEntry(long sw, long srcPort, long dstPort,
MACAddress srcMac, MACAddress dstMac,
@@ -30,13 +31,13 @@
this.actions = new HashSet<Action>();
this.actions.add(new ForwardAction(dstPort));
this.operator = operator;
-
+ this.flowEntryId = hashCode();
}
/***
* Gets hard timeout value in seconds.
*
- * @return hardTimeout
+ * @return the hard timeout value in seconds
*/
public int getHardTimeout() {
return hardTimeout;
@@ -45,7 +46,7 @@
/***
* Gets idle timeout value in seconds.
*
- * @return idleTimeout
+ * @return the idle timeout value in seconds
*/
public int getIdleTimeout() {
return idleTimeout;
@@ -54,7 +55,7 @@
/***
* Sets hard timeout value in seconds.
*
- * @param hardTimeout
+ * @param the hard timeout value in seconds
*/
public void setHardTimeout(int hardTimeout) {
this.hardTimeout = hardTimeout;
@@ -63,12 +64,30 @@
/***
* Sets idle timeout value in seconds.
*
- * @param idleTimeout
+ * @param the idle timeout value in seconds
*/
public void setIdleTimeout(int idleTimeout) {
this.idleTimeout = idleTimeout;
}
+ /***
+ * Gets flowEntryId.
+ *
+ * @param the flowEntryId to be set in cookie
+ */
+ public long getFlowEntryId() {
+ return flowEntryId;
+ }
+
+ /***
+ * Sets flowEntryId.
+ *
+ * @param the flowEntryId to be set in cookie
+ */
+ public void setFlowEntryId(long flowEntryId) {
+ this.flowEntryId = flowEntryId;
+ }
+
@Override
public String toString() {
return match + "->" + actions;
@@ -89,7 +108,7 @@
public net.onrc.onos.core.util.FlowEntry getFlowEntry() {
net.onrc.onos.core.util.FlowEntry entry = new net.onrc.onos.core.util.FlowEntry();
entry.setDpid(new Dpid(sw));
- entry.setFlowEntryId(new FlowEntryId(hashCode())); // naive, but useful for now
+ entry.setFlowEntryId(new FlowEntryId(flowEntryId));
entry.setFlowEntryMatch(match.getFlowEntryMatch());
FlowEntryActions flowEntryActions = new FlowEntryActions();
for (Action action : actions) {
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java b/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
index 136a357..320126c 100644
--- a/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
@@ -89,7 +89,7 @@
private IRestApiService restApi;
private IEventChannel<Long, IntentOperationList> opEventChannel;
- private final ReentrantLock lock = new ReentrantLock();
+ private final ReentrantLock lock = new ReentrantLock(true);
private HashSet<LinkEvent> unmatchedLinkEvents = new HashSet<>();
private Map<String, Set<Long>> intentInstalledMap = new ConcurrentHashMap<String, Set<Long>>();
private static final String INTENT_OP_EVENT_CHANNEL_NAME = "onos.pathintent";
@@ -173,15 +173,16 @@
@Override
public IntentOperationList executeIntentOperations(IntentOperationList list) {
+
if (list == null || list.size() == 0) {
return null;
}
- PerfLogger p = new PerfLogger("executeIntentOperations_" + list.get(0).operator);
lock.lock(); // TODO optimize locking using smaller steps
try {
+ log.trace("lock executeIntentOperations, lock obj is already locked? {}", lock.isLocked());
// update the map of high-level intents
- p.log("begin_updateInMemoryIntents");
+
highLevelIntents.executeOperations(list);
// change states of high-level intents
@@ -214,21 +215,15 @@
}
}
highLevelIntents.changeStates(states);
- p.log("end_updateInMemoryIntents");
// calculate path-intents (low-level operations)
- p.log("begin_calcPathIntents");
IntentOperationList pathIntentOperations = runtime.calcPathIntents(list, highLevelIntents, pathIntents);
- p.log("end_calcPathIntents");
// persist calculated low-level operations into data store
- p.log("begin_persistPathIntents");
long key = persistIntent.getKey();
persistIntent.persistIfLeader(key, pathIntentOperations);
- p.log("end_persistPathIntents");
// remove error-intents and reflect them to high-level intents
- p.log("begin_removeErrorIntents");
states.clear();
Iterator<IntentOperation> i = pathIntentOperations.iterator();
while (i.hasNext()) {
@@ -239,15 +234,11 @@
}
}
highLevelIntents.changeStates(states);
- p.log("end_removeErrorIntents");
// update the map of path intents and publish the path operations
- p.log("begin_updateInMemoryPathIntents");
pathIntents.executeOperations(pathIntentOperations);
- p.log("end_updateInMemoryPathIntents");
// XXX Demo special: add a complete path to remove operation
- p.log("begin_addPathToRemoveOperation");
for (IntentOperation op : pathIntentOperations) {
if (op.operator.equals(Operator.REMOVE)) {
op.intent = pathIntents.getIntent(op.intent.getId());
@@ -256,19 +247,16 @@
log.debug("operation: {}, intent:{}", op.operator, op.intent);
}
}
- p.log("end_addPathToRemoveOperation");
// send notification
- p.log("begin_sendNotification");
// XXX: Send notifications using the same key every time
// and receive them by entryAdded() and entryUpdated()
opEventChannel.addEntry(0L, pathIntentOperations);
- p.log("end_sendNotification");
//opEventChannel.removeEntry(key);
return pathIntentOperations;
} finally {
lock.unlock();
- p.flushLog();
+ log.trace("unlock executeIntentOperations");
}
}
@@ -380,11 +368,11 @@
@Override
public void entryUpdated(IntentStateList value) {
// TODO draw state transition diagram in multiple ONOS instances and update this method
- PerfLogger p = new PerfLogger("entryUpdated");
+
lock.lock(); // TODO optimize locking using smaller steps
try {
+ log.trace("lock entryUpdated, lock obj is already locked? {}", lock.isLocked());
// reflect state changes of path-level intent into application-level intents
- p.log("begin_changeStateByNotification");
IntentStateList highLevelIntentStates = new IntentStateList();
IntentStateList pathIntentStates = new IntentStateList();
for (Entry<String, IntentState> entry : value.entrySet()) {
@@ -401,8 +389,6 @@
}
IntentState state = entry.getValue();
- log.debug("put the state pathIntentStates ID {}, state {}", entry.getKey(), state);
-
switch (state) {
case INST_ACK:
Set<Long> installedDpids = calcInstalledDpids(pathIntent, value.domainSwitchDpids);
@@ -416,10 +402,19 @@
// FALLTHROUGH
// case DEL_REQ:
// FALLTHROUGH
- case DEL_ACK:
- // FALLTHROUGH
case DEL_PENDING:
+ log.debug("put the state highLevelIntentStates ID {}, state {}", parentIntent.getId(), state);
highLevelIntentStates.put(parentIntent.getId(), state);
+ log.debug("put the state pathIntentStates ID {}, state {}", entry.getKey(), entry.getValue());
+ pathIntentStates.put(entry.getKey(), entry.getValue());
+ break;
+ case DEL_ACK:
+ if (intentInstalledMap.containsKey(parentIntent.getId())) {
+ intentInstalledMap.remove(parentIntent.getId());
+ }
+ log.debug("put the state highLevelIntentStates ID {}, state {}", parentIntent.getId(), state);
+ highLevelIntentStates.put(parentIntent.getId(), state);
+ log.debug("put the state pathIntentStates ID {}, state {}", entry.getKey(), entry.getValue());
pathIntentStates.put(entry.getKey(), entry.getValue());
break;
default:
@@ -428,15 +423,15 @@
}
highLevelIntents.changeStates(highLevelIntentStates);
pathIntents.changeStates(pathIntentStates);
- p.log("end_changeStateByNotification");
} finally {
lock.unlock();
- p.flushLog();
+ log.trace("unlock entryUpdated");
}
}
/***
* This function is to check whether the entire path's flow entries are installed or not.
+ *
* @param pathIntent : The pathIntent to be checked
* @param installedDpids : The dpids installed on one ONOS instance
* @return The result of whether a pathIntent has been installed or not.
@@ -492,7 +487,7 @@
}
if (log.isTraceEnabled()) {
- log.trace("All switches {}, domain switch dpids {}", allSwitchesForPath, domainSwitchDpids);
+ log.trace("All switches for a path {}, domain switch dpids {}", allSwitchesForPath, domainSwitchDpids);
}
return allSwitchesForPath;
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PlanCalcRuntime.java b/src/main/java/net/onrc/onos/core/intent/runtime/PlanCalcRuntime.java
index 73a31b8..bbd2555 100644
--- a/src/main/java/net/onrc/onos/core/intent/runtime/PlanCalcRuntime.java
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PlanCalcRuntime.java
@@ -61,6 +61,7 @@
long lastDstSw = -1, lastDstPort = -1, firstSrcSw = -1;
MACAddress srcMac, dstMac;
int idleTimeout = 0, hardTimeout = 0, firstSwitchIdleTimeout = 0, firstSwitchHardTimeout = 0;
+ Long cookieId = null;
if (parent instanceof ShortestPathIntent) {
ShortestPathIntent pathIntent = (ShortestPathIntent) parent;
// Switch srcSwitch = graph.getSwitch(pathIntent.getSrcSwitchDpid());
@@ -77,6 +78,11 @@
hardTimeout = pathIntent.getHardTimeout();
firstSwitchIdleTimeout = pathIntent.getFirstSwitchIdleTimeout();
firstSwitchHardTimeout = pathIntent.getFirstSwitchHardTimetout();
+ try {
+ cookieId = Long.valueOf(pathIntent.getId());
+ } catch (NumberFormatException e) {
+ log.trace("NumberFormatException : ", e);
+ }
} else {
log.warn("Unsupported Intent: {}", parent);
continue;
@@ -99,6 +105,10 @@
fe.setIdleTimeout(firstSwitchIdleTimeout);
fe.setHardTimeout(firstSwitchHardTimeout);
}
+ if (cookieId != null) {
+ log.trace("cookieId is set: {}", cookieId);
+ fe.setFlowEntryId(cookieId);
+ }
entries.add(fe);
// srcPort = link.getDstPort();
srcPort = linkEvent.getDst().getNumber();
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallModule.java b/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallModule.java
index 94e5d8f..2d2dbd8 100644
--- a/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallModule.java
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallModule.java
@@ -6,9 +6,14 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;
+import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -18,15 +23,21 @@
import net.onrc.onos.core.datagrid.IEventChannelListener;
import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
import net.onrc.onos.core.intent.FlowEntry;
+import net.onrc.onos.core.intent.Intent;
import net.onrc.onos.core.intent.Intent.IntentState;
import net.onrc.onos.core.intent.IntentOperation;
import net.onrc.onos.core.intent.IntentOperationList;
+import net.onrc.onos.core.intent.PathIntent;
+import net.onrc.onos.core.intent.ShortestPathIntent;
import net.onrc.onos.core.topology.ITopologyService;
+import org.openflow.protocol.OFFlowRemoved;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class PlanInstallModule implements IFloodlightModule {
+public class PlanInstallModule implements IFloodlightModule, IOFMessageListener {
protected volatile IFloodlightProviderService floodlightProvider;
protected volatile ITopologyService topologyService;
protected volatile IDatagridService datagridService;
@@ -39,19 +50,7 @@
private static final String PATH_INTENT_CHANNEL_NAME = "onos.pathintent";
private static final String INTENT_STATE_EVENT_CHANNEL_NAME = "onos.pathintent_state";
-
-
- @Override
- public void init(FloodlightModuleContext context)
- throws FloodlightModuleException {
- floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
- topologyService = context.getServiceImpl(ITopologyService.class);
- datagridService = context.getServiceImpl(IDatagridService.class);
- flowPusher = context.getServiceImpl(IFlowPusherService.class);
- planCalc = new PlanCalcRuntime();
- planInstall = new PlanInstallRuntime(floodlightProvider, flowPusher);
- eventListener = new EventListener();
- }
+ private ConcurrentMap<String, Intent> parentIntentMap = new ConcurrentHashMap<String, Intent>();
class EventListener extends Thread
implements IEventChannelListener<Long, IntentOperationList> {
@@ -129,7 +128,8 @@
}
if (log.isTraceEnabled()) {
- log.trace("domainSwitchDpids {}", states.domainSwitchDpids);
+ log.trace("sendNotifications, states {}, domainSwitchDpids {}",
+ states, states.domainSwitchDpids);
}
intentStateChannel.addTransientEntry(key, states);
@@ -150,6 +150,7 @@
@Override
public void entryUpdated(IntentOperationList value) {
+ putIntentOpsInfoInParentMap(value);
log("start_intentNotifRecv");
log("begin_sendReceivedNotif");
sendNotifications(value, false, false, null);
@@ -163,6 +164,23 @@
log.warn("Error putting to intent queue: {}", e.getMessage());
}
}
+
+ private void putIntentOpsInfoInParentMap(IntentOperationList intentOps) {
+ for (IntentOperation i : intentOps) {
+ if (!(i.intent instanceof PathIntent)) {
+ log.warn("Not a path intent: {}", i);
+ continue;
+ }
+ PathIntent intent = (PathIntent) i.intent;
+ Intent parent = intent.getParentIntent();
+ if (parent instanceof ShortestPathIntent) {
+ parentIntentMap.put(parent.getId(), parent);
+ } else {
+ log.warn("Unsupported Intent: {}", parent);
+ continue;
+ }
+ }
+ }
}
public static void log(String step) {
@@ -170,6 +188,18 @@
}
@Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ topologyService = context.getServiceImpl(ITopologyService.class);
+ datagridService = context.getServiceImpl(IDatagridService.class);
+ flowPusher = context.getServiceImpl(IFlowPusherService.class);
+ planCalc = new PlanCalcRuntime();
+ planInstall = new PlanInstallRuntime(floodlightProvider, flowPusher);
+ eventListener = new EventListener();
+ }
+
+ @Override
public void startUp(FloodlightModuleContext context) {
// start subscriber
datagridService.addListener(PATH_INTENT_CHANNEL_NAME,
@@ -181,6 +211,7 @@
intentStateChannel = datagridService.createChannel(INTENT_STATE_EVENT_CHANNEL_NAME,
Long.class,
IntentStateList.class);
+ floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
}
@Override
@@ -205,4 +236,84 @@
// no services, for now
return null;
}
+
+ @Override
+ public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+ if (msg.getType().equals(OFType.FLOW_REMOVED) &&
+ (msg instanceof OFFlowRemoved)) {
+ OFFlowRemoved flowRemovedMsg = (OFFlowRemoved) msg;
+
+ if (log.isTraceEnabled()) {
+ log.trace("Receive flowRemoved from sw {} : Cookie {}",
+ sw.getId(), flowRemovedMsg.getCookie());
+ }
+
+ String intentParentId = Long.toString(flowRemovedMsg.getCookie());
+ Intent intent = parentIntentMap.get(intentParentId);
+
+ //We assume if the path src sw flow entry is expired,
+ //the path is expired.
+ if (!isFlowSrcRemoved(sw.getId(), intentParentId)) {
+ return Command.CONTINUE;
+ }
+
+ ShortestPathIntent spfIntent = null;
+ if (!(intent instanceof ShortestPathIntent)) {
+ return Command.CONTINUE;
+ }
+ spfIntent = (ShortestPathIntent) intent;
+ String pathIntentId = spfIntent.getPathIntentId();
+
+ IntentStateList states = new IntentStateList();
+ IntentState newState = IntentState.DEL_ACK;
+ states.put(pathIntentId, newState);
+ Set<Long> domainSwitchDpids = floodlightProvider.getSwitches().keySet();
+ if (domainSwitchDpids != null) {
+ states.domainSwitchDpids.addAll(domainSwitchDpids);
+ }
+ parentIntentMap.remove(intentParentId);
+ log.debug("addEntry to intentStateChannel intentId {}, states {}", flowRemovedMsg.getCookie(), states);
+
+ intentStateChannel.addTransientEntry(flowRemovedMsg.getCookie(), states);
+ }
+
+ return Command.CONTINUE;
+ }
+
+ private boolean isFlowSrcRemoved(long dpid, String shortestPathIntentId) {
+ Intent intent = parentIntentMap.get(shortestPathIntentId);
+ ShortestPathIntent spfIntent = null;
+ if (intent instanceof ShortestPathIntent) {
+ spfIntent = (ShortestPathIntent) intent;
+ }
+
+ if (spfIntent == null) {
+ return false;
+ }
+
+ long srcSwDpid = spfIntent.getSrcSwitchDpid();
+ if (srcSwDpid == dpid) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isCallbackOrderingPrereq(OFType type, String name) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isCallbackOrderingPostreq(OFType type, String name) {
+ // TODO Auto-generated method stub
+ return false;
+ }
}