blob: c69d6ee23847b6c272138ba4349edcac74a572d9 [file] [log] [blame]
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001package net.floodlightcontroller.flowcache;
2
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08003import java.io.IOException;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08004import java.util.ArrayList;
5import java.util.Collection;
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +00006import java.util.Collections;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08007import java.util.EnumSet;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08008import java.util.HashMap;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07009import java.util.HashSet;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +000010import java.util.LinkedList;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080011import java.util.List;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080012import java.util.Map;
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +000013import java.util.Random;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070014import java.util.TreeMap;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070015import java.util.concurrent.BlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080016import java.util.concurrent.Executors;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070017import java.util.concurrent.LinkedBlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080018import java.util.concurrent.ScheduledExecutorService;
19import java.util.concurrent.ScheduledFuture;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070020import java.util.concurrent.ThreadPoolExecutor;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080021import java.util.concurrent.TimeUnit;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080022
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080023import net.floodlightcontroller.core.IFloodlightProviderService;
24import net.floodlightcontroller.core.INetMapStorage;
25import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
26import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070027import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
Pankaj Berded0079742013-03-27 17:53:25 -070028import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070029import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080030import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080031import net.floodlightcontroller.core.module.FloodlightModuleContext;
32import net.floodlightcontroller.core.module.FloodlightModuleException;
33import net.floodlightcontroller.core.module.IFloodlightModule;
34import net.floodlightcontroller.core.module.IFloodlightService;
35import net.floodlightcontroller.flowcache.IFlowService;
36import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
37import net.floodlightcontroller.restserver.IRestApiService;
38import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080039import net.floodlightcontroller.util.DataPath;
40import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080041import net.floodlightcontroller.util.DataPathEndpoints;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080042import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070043import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080044import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070045import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080046import net.floodlightcontroller.util.FlowEntrySwitchState;
47import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080048import net.floodlightcontroller.util.FlowId;
49import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070050import net.floodlightcontroller.util.IPv4Net;
51import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080052import net.floodlightcontroller.util.OFMessageDamper;
53import net.floodlightcontroller.util.Port;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070054import net.floodlightcontroller.util.SwitchPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080055import net.onrc.onos.util.GraphDBConnection;
56import net.onrc.onos.util.GraphDBConnection.Transaction;
57
58import org.openflow.protocol.OFFlowMod;
59import org.openflow.protocol.OFMatch;
60import org.openflow.protocol.OFPacketOut;
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070061import org.openflow.protocol.OFPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080062import org.openflow.protocol.OFType;
63import org.openflow.protocol.action.OFAction;
64import org.openflow.protocol.action.OFActionOutput;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080065
66import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
68
Pavlin Radoslavov5adf1522013-04-04 17:43:41 -070069public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080070
71 public GraphDBConnection conn;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080072
73 protected IRestApiService restApi;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080074 protected IFloodlightProviderService floodlightProvider;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070075 protected ITopoRouteService topoRouteService;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070076 protected FloodlightModuleContext context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080077
78 protected OFMessageDamper messageDamper;
79
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070080 //
81 // TODO: Values copied from elsewhere (class LearningSwitch).
82 // The local copy should go away!
83 //
84 protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
85 protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
86 public static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0; // infinity
87 public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
88 public static final short PRIORITY_DEFAULT = 100;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080089
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +000090 // Flow Entry ID generation state
91 private static Random randomGenerator = new Random();
92 private static int nextFlowEntryIdPrefix = 0;
93 private static int nextFlowEntryIdSuffix = 0;
94 private static long nextFlowEntryId = 0;
95
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070096 private static long measurementFlowId = 100000;
97 private static String measurementFlowIdStr = "0x186a0"; // 100000
98 private long modifiedMeasurementFlowTime = 0;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070099
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800100 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800101 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
102
103 // The periodic task(s)
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700104 private final ScheduledExecutorService mapReaderScheduler =
105 Executors.newScheduledThreadPool(1);
106
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700107 final Runnable mapReader = new Runnable() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800108 public void run() {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700109 long startTime = System.nanoTime();
110 int counterAllFlowEntries = 0;
111 int counterMyNotUpdatedFlowEntries = 0;
112 int counterAllFlowPaths = 0;
113 int counterMyFlowPaths = 0;
114
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800115 if (floodlightProvider == null) {
116 log.debug("FloodlightProvider service not found!");
117 return;
118 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000119 Map<Long, IOFSwitch> mySwitches =
120 floodlightProvider.getSwitches();
121 Map<Long, IFlowEntry> myFlowEntries =
122 new TreeMap<Long, IFlowEntry>();
123 LinkedList<IFlowEntry> deleteFlowEntries =
124 new LinkedList<IFlowEntry>();
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700125
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700126
127 //
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700128 // Fetch all Flow Entries and select only my Flow Entries
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700129 // that need to be updated into the switches.
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700130 //
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000131 Iterable<IFlowEntry> allFlowEntries =
132 conn.utils().getAllFlowEntries(conn);
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700133 for (IFlowEntry flowEntryObj : allFlowEntries) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700134 counterAllFlowEntries++;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000135 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700136 String userState = flowEntryObj.getUserState();
137 String switchState = flowEntryObj.getSwitchState();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000138 String dpidStr = flowEntryObj.getSwitchDpid();
139 if ((flowEntryIdStr == null) ||
140 (userState == null) ||
141 (switchState == null) ||
142 (dpidStr == null)) {
143 log.debug("IGNORING Flow Entry entry with null fields");
144 continue;
145 }
146 FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
147 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800148
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000149 if (! switchState.equals("FE_SWITCH_NOT_UPDATED"))
150 continue; // Ignore the entry: nothing to do
151
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800152 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000153 if (mySwitch == null)
154 continue; // Ignore the entry: not my switch
155
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700156 myFlowEntries.put(flowEntryId.value(), flowEntryObj);
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700157 if (userState.equals("FE_USER_DELETE")) {
158 // An entry that needs to be deleted.
159 deleteFlowEntries.add(flowEntryObj);
160 }
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700161 }
162
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700163 log.debug("MEASUREMENT: Found {} My Flow Entries NOT_UPDATED",
164 myFlowEntries.size());
165
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700166 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700167 // Process my Flow Entries in the Flow Entry ID order
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700168 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700169 boolean processed_measurement_flow = false;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700170 for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700171 counterMyNotUpdatedFlowEntries++;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700172 IFlowEntry flowEntryObj = entry.getValue();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700173 IFlowPath flowObj =
174 conn.utils().getFlowPathByFlowEntry(conn,
175 flowEntryObj);
176 if (flowObj == null)
177 continue; // Should NOT happen
178
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700179 // Code for measurement purpose
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700180 // TODO: Commented-out for now
181 /*
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700182 {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700183 if (flowObj.getFlowId().equals(measurementFlowIdStr)) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700184 processed_measurement_flow = true;
185 }
186 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700187 */
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700188
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700189 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700190 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000191 if (mySwitch == null)
192 continue; // Shouldn't happen
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700193 installFlowEntry(mySwitch, flowObj, flowEntryObj);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800194 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000195
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700196 log.debug("MEASUREMENT: Found {} Flow Entries to delete",
197 deleteFlowEntries.size());
198
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000199 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700200 // Delete all entries marked for deletion from the
201 // Network MAP.
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000202 //
203 // TODO: We should use the OpenFlow Barrier mechanism
204 // to check for errors, and delete the Flow Entries after the
205 // Barrier message is received.
206 //
207 while (! deleteFlowEntries.isEmpty()) {
208 IFlowEntry flowEntryObj = deleteFlowEntries.poll();
209 IFlowPath flowObj =
210 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
211 if (flowObj == null) {
212 log.debug("Did not find FlowPath to be deleted");
213 continue;
214 }
215 flowObj.removeFlowEntry(flowEntryObj);
216 conn.utils().removeFlowEntry(conn, flowEntryObj);
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000217 }
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700218
219
220 //
221 // Fetch and recompute the Shortest Path for those
222 // Flow Paths this controller is responsible for.
223 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700224 topoRouteService.prepareShortestPathTopo();
225 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
226 HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
227 for (IFlowPath flowPathObj : allFlowPaths) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700228 counterAllFlowPaths++;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700229 if (flowPathObj == null)
230 continue;
231 String dataPathSummaryStr = flowPathObj.getDataPathSummary();
232 if (dataPathSummaryStr == null)
233 continue; // Could be invalid entry?
234 if (dataPathSummaryStr.isEmpty())
235 continue; // No need to maintain this flow
236
237 // Fetch the fields needed to recompute the shortest path
238 String flowIdStr = flowPathObj.getFlowId();
239 String srcDpidStr = flowPathObj.getSrcSwitch();
240 Short srcPortShort = flowPathObj.getSrcPort();
241 String dstDpidStr = flowPathObj.getDstSwitch();
242 Short dstPortShort = flowPathObj.getDstPort();
243 if ((flowIdStr == null) ||
244 (srcDpidStr == null) ||
245 (srcPortShort == null) ||
246 (dstDpidStr == null) ||
247 (dstPortShort == null)) {
248 log.debug("IGNORING Flow Path entry with null fields");
249 continue;
250 }
251
252 FlowId flowId = new FlowId(flowIdStr);
253 Dpid srcDpid = new Dpid(srcDpidStr);
254 Port srcPort = new Port(srcPortShort);
255 Dpid dstDpid = new Dpid(dstDpidStr);
256 Port dstPort = new Port(dstPortShort);
257 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
258 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
Pavlin Radoslavov2659a0b2013-04-03 20:30:40 -0700259
260 //
261 // Use the source DPID as a heuristic to decide
262 // which controller is responsible for maintaining the
263 // shortest path.
264 // NOTE: This heuristic is error-prone: if the switch
265 // goes away and no controller is responsible for that
266 // switch, then the original Flow Path is not cleaned-up
267 //
268 IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
269 if (mySwitch == null)
270 continue; // Ignore: not my responsibility
271
272 counterMyFlowPaths++;
273
Pavlin Radoslavov832aa652013-03-29 16:21:59 -0700274 //
275 // NOTE: Using here the regular getShortestPath() method
276 // won't work here, because that method calls internally
277 // "conn.endTx(Transaction.COMMIT)", and that will
278 // invalidate all handlers to the Titan database.
279 // If we want to experiment with calling here
280 // getShortestPath(), we need to refactor that code
281 // to avoid closing the transaction.
282 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700283 DataPath dataPath =
284 topoRouteService.getTopoShortestPath(srcSwitchPort,
285 dstSwitchPort);
Pavlin Radoslavov4a325822013-04-02 22:16:59 +0000286 if (dataPath == null) {
287 // We need the DataPath to compare the paths
288 dataPath = new DataPath();
289 dataPath.setSrcPort(srcSwitchPort);
290 dataPath.setDstPort(dstSwitchPort);
291 }
292
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700293 String newDataPathSummaryStr = dataPath.dataPathSummary();
294 if (dataPathSummaryStr.equals(newDataPathSummaryStr))
295 continue; // Nothing changed
296
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700297 log.debug("RECONCILE: Need to Reconcile Shortest Path for FlowID {}",
298 flowId.toString());
299 flowObjSet.add(flowPathObj);
300 }
Pavlin Radoslavov53a3a8c2013-04-04 04:34:50 -0700301 log.debug("MEASUREMENT: Found {} Flows to reconcile",
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700302 flowObjSet.size());
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700303 reconcileFlows(flowObjSet);
304 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700305
306
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800307 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700308
309 if (processed_measurement_flow) {
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700310 long estimatedTime =
311 System.nanoTime() - modifiedMeasurementFlowTime;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700312 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
313 (double)estimatedTime / 1000000000 + " sec";
314 log.debug(logMsg);
315 }
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700316
317 long estimatedTime = System.nanoTime() - startTime;
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700318 double rate = 0.0;
319 if (estimatedTime > 0)
320 rate = ((double)counterAllFlowPaths * 1000000000) / estimatedTime;
321 String logMsg = "MEASUREMENT: Processed AllFlowEntries: " +
322 counterAllFlowEntries + " MyNotUpdatedFlowEntries: " +
323 counterMyNotUpdatedFlowEntries + " AllFlowPaths: " +
324 counterAllFlowPaths + " MyFlowPaths: " +
325 counterMyFlowPaths + " in " +
326 (double)estimatedTime / 1000000000 + " sec: " +
327 rate + " paths/s";
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700328 log.debug(logMsg);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800329 }
330 };
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700331
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700332 final ScheduledFuture<?> mapReaderHandle =
333 mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800334
335 @Override
336 public void init(String conf) {
337 conn = GraphDBConnection.getInstance(conf);
338 }
339
340 public void finalize() {
341 close();
342 }
343
344 @Override
345 public void close() {
346 conn.close();
347 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800348
349 @Override
350 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
351 Collection<Class<? extends IFloodlightService>> l =
352 new ArrayList<Class<? extends IFloodlightService>>();
353 l.add(IFlowService.class);
354 return l;
355 }
356
357 @Override
358 public Map<Class<? extends IFloodlightService>, IFloodlightService>
359 getServiceImpls() {
360 Map<Class<? extends IFloodlightService>,
361 IFloodlightService> m =
362 new HashMap<Class<? extends IFloodlightService>,
363 IFloodlightService>();
364 m.put(IFlowService.class, this);
365 return m;
366 }
367
368 @Override
369 public Collection<Class<? extends IFloodlightService>>
370 getModuleDependencies() {
371 Collection<Class<? extends IFloodlightService>> l =
372 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800373 l.add(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700374 l.add(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800375 l.add(IRestApiService.class);
376 return l;
377 }
378
379 @Override
380 public void init(FloodlightModuleContext context)
381 throws FloodlightModuleException {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700382 this.context = context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800383 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700384 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800385 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800386 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
387 EnumSet.of(OFType.FLOW_MOD),
388 OFMESSAGE_DAMPER_TIMEOUT);
389 // TODO: An ugly hack!
390 String conf = "/tmp/cassandra.titan";
391 this.init(conf);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800392 }
393
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000394 private long getNextFlowEntryId() {
395 //
396 // Generate the next Flow Entry ID.
397 // NOTE: For now, the higher 32 bits are random, and
398 // the lower 32 bits are sequential.
399 // In the future, we need a better allocation mechanism.
400 //
401 if ((nextFlowEntryIdSuffix & 0xffffffffL) == 0xffffffffL) {
402 nextFlowEntryIdPrefix = randomGenerator.nextInt();
403 nextFlowEntryIdSuffix = 0;
404 } else {
405 nextFlowEntryIdSuffix++;
406 }
407 long result = (long)nextFlowEntryIdPrefix << 32;
408 result = result | (0xffffffffL & nextFlowEntryIdSuffix);
409 return result;
410 }
411
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800412 @Override
413 public void startUp(FloodlightModuleContext context) {
414 restApi.addRestletRoutable(new FlowWebRoutable());
Pavlin Radoslavov80ca6302013-03-20 02:08:09 -0700415
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000416 // Initialize the Flow Entry ID generator
417 nextFlowEntryIdPrefix = randomGenerator.nextInt();
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800418 }
419
420 /**
421 * Add a flow.
422 *
423 * Internally, ONOS will automatically register the installer for
424 * receiving Flow Path Notifications for that path.
425 *
426 * @param flowPath the Flow Path to install.
427 * @param flowId the return-by-reference Flow ID as assigned internally.
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700428 * @param dataPathSummaryStr the data path summary string if the added
429 * flow will be maintained internally, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800430 * @return true on success, otherwise false.
431 */
432 @Override
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700433 public boolean addFlow(FlowPath flowPath, FlowId flowId,
434 String dataPathSummaryStr) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700435 /*
436 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700437 if (flowPath.flowId().value() == measurementFlowId) {
438 modifiedMeasurementFlowTime = System.nanoTime();
439 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700440 */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800441
442 //
443 // Assign the FlowEntry IDs
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700444 // Right now every new flow entry gets a new flow entry ID
445 // TODO: This needs to be redesigned!
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800446 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800447 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000448 long id = getNextFlowEntryId();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800449 flowEntry.setFlowEntryId(new FlowEntryId(id));
450 }
451
452 IFlowPath flowObj = null;
453 try {
454 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
455 != null) {
456 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
457 flowPath.flowId().toString());
458 } else {
459 flowObj = conn.utils().newFlowPath(conn);
460 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
461 flowPath.flowId().toString());
462 }
463 } catch (Exception e) {
464 // TODO: handle exceptions
465 conn.endTx(Transaction.ROLLBACK);
466 log.error(":addFlow FlowId:{} failed",
467 flowPath.flowId().toString());
468 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700469 if (flowObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000470 log.error(":addFlow FlowId:{} failed: Flow object not created",
471 flowPath.flowId().toString());
472 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800473 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700474 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800475
476 //
477 // Set the Flow key:
478 // - flowId
479 //
480 flowObj.setFlowId(flowPath.flowId().toString());
481 flowObj.setType("flow");
482
483 //
484 // Set the Flow attributes:
485 // - flowPath.installerId()
486 // - flowPath.dataPath().srcPort()
487 // - flowPath.dataPath().dstPort()
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700488 // - flowPath.matchEthernetFrameType()
489 // - flowPath.matchSrcIPv4Net()
490 // - flowPath.matchDstIPv4Net()
491 // - flowPath.matchSrcMac()
492 // - flowPath.matchDstMac()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800493 //
494 flowObj.setInstallerId(flowPath.installerId().toString());
495 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
496 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
497 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
498 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700499 if (flowPath.flowEntryMatch().matchEthernetFrameType()) {
500 flowObj.setMatchEthernetFrameType(flowPath.flowEntryMatch().ethernetFrameType());
501 }
502 if (flowPath.flowEntryMatch().matchSrcIPv4Net()) {
503 flowObj.setMatchSrcIPv4Net(flowPath.flowEntryMatch().srcIPv4Net().toString());
504 }
505 if (flowPath.flowEntryMatch().matchDstIPv4Net()) {
506 flowObj.setMatchDstIPv4Net(flowPath.flowEntryMatch().dstIPv4Net().toString());
507 }
508 if (flowPath.flowEntryMatch().matchSrcMac()) {
509 flowObj.setMatchSrcMac(flowPath.flowEntryMatch().srcMac().toString());
510 }
511 if (flowPath.flowEntryMatch().matchDstMac()) {
512 flowObj.setMatchDstMac(flowPath.flowEntryMatch().dstMac().toString());
513 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800514
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700515 if (dataPathSummaryStr != null) {
516 flowObj.setDataPathSummary(dataPathSummaryStr);
517 } else {
518 flowObj.setDataPathSummary("");
519 }
520
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800521 // Flow edges:
522 // HeadFE
523
524
525 //
526 // Flow Entries:
527 // flowPath.dataPath().flowEntries()
528 //
529 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
530 IFlowEntry flowEntryObj = null;
531 boolean found = false;
532 try {
533 if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
534 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
535 flowEntry.flowEntryId().toString());
536 found = true;
537 } else {
538 flowEntryObj = conn.utils().newFlowEntry(conn);
539 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
540 flowEntry.flowEntryId().toString());
541 }
542 } catch (Exception e) {
543 // TODO: handle exceptions
544 conn.endTx(Transaction.ROLLBACK);
545 log.error(":addFlow FlowEntryId:{} failed",
546 flowEntry.flowEntryId().toString());
547 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700548 if (flowEntryObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000549 log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created",
550 flowEntry.flowEntryId().toString());
551 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800552 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700553 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800554
555 //
556 // Set the Flow Entry key:
557 // - flowEntry.flowEntryId()
558 //
559 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
560 flowEntryObj.setType("flow_entry");
561
562 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700563 // Set the Flow Entry Edges and attributes:
564 // - Switch edge
565 // - InPort edge
566 // - OutPort edge
567 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800568 // - flowEntry.flowEntryMatch()
569 // - flowEntry.flowEntryActions()
570 // - flowEntry.dpid()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800571 // - flowEntry.flowEntryUserState()
572 // - flowEntry.flowEntrySwitchState()
573 // - flowEntry.flowEntryErrorState()
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700574 // - flowEntry.matchInPort()
575 // - flowEntry.matchEthernetFrameType()
576 // - flowEntry.matchSrcIPv4Net()
577 // - flowEntry.matchDstIPv4Net()
578 // - flowEntry.matchSrcMac()
579 // - flowEntry.matchDstMac()
580 // - flowEntry.actionOutput()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800581 //
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700582 ISwitchObject sw =
583 conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800584 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
Pankaj Berded0079742013-03-27 17:53:25 -0700585 flowEntryObj.setSwitch(sw);
586 if (flowEntry.flowEntryMatch().matchInPort()) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700587 IPortObject inport =
588 conn.utils().searchPort(conn, flowEntry.dpid().toString(),
589 flowEntry.flowEntryMatch().inPort().value());
Pankaj Berded0079742013-03-27 17:53:25 -0700590 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
591 flowEntryObj.setInPort(inport);
592 }
593 if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
594 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
595 }
596 if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
597 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
598 }
599 if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
600 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
601 }
602 if (flowEntry.flowEntryMatch().matchSrcMac()) {
603 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
604 }
605 if (flowEntry.flowEntryMatch().matchDstMac()) {
606 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
607 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700608
609 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
Pankaj Berded0079742013-03-27 17:53:25 -0700610 if (fa.actionOutput() != null) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700611 IPortObject outport =
612 conn.utils().searchPort(conn,
613 flowEntry.dpid().toString(),
614 fa.actionOutput().port().value());
615 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
616 flowEntryObj.setOutPort(outport);
Pankaj Berded0079742013-03-27 17:53:25 -0700617 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700618 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800619 // TODO: Hacks with hard-coded state names!
620 if (found)
621 flowEntryObj.setUserState("FE_USER_MODIFY");
622 else
623 flowEntryObj.setUserState("FE_USER_ADD");
624 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
625 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700626 // TODO: Take care of the FlowEntryErrorState.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800627 //
628
629 // Flow Entries edges:
630 // Flow
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700631 // NextFE (TODO)
632 if (! found) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800633 flowObj.addFlowEntry(flowEntryObj);
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700634 flowEntryObj.setFlow(flowObj);
635 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800636 }
637 conn.endTx(Transaction.COMMIT);
638
639 //
640 // TODO: We need a proper Flow ID allocation mechanism.
641 //
642 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700643
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800644 return true;
645 }
646
647 /**
648 * Delete a previously added flow.
649 *
650 * @param flowId the Flow ID of the flow to delete.
651 * @return true on success, otherwise false.
652 */
653 @Override
654 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700655 /*
656 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700657 if (flowId.value() == measurementFlowId) {
658 modifiedMeasurementFlowTime = System.nanoTime();
659 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700660 */
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700661
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800662 IFlowPath flowObj = null;
663 //
664 // We just mark the entries for deletion,
665 // and let the switches remove each individual entry after
666 // it has been removed from the switches.
667 //
668 try {
669 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
670 != null) {
671 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
672 flowId.toString());
673 } else {
674 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
675 flowId.toString());
676 }
677 } catch (Exception e) {
678 // TODO: handle exceptions
679 conn.endTx(Transaction.ROLLBACK);
680 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
681 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700682 if (flowObj == null) {
683 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800684 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700685 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800686
687 //
688 // Find and mark for deletion all Flow Entries
689 //
690 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
691 boolean empty = true; // TODO: an ugly hack
692 for (IFlowEntry flowEntryObj : flowEntries) {
693 empty = false;
694 // flowObj.removeFlowEntry(flowEntryObj);
695 // conn.utils().removeFlowEntry(conn, flowEntryObj);
696 flowEntryObj.setUserState("FE_USER_DELETE");
697 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
698 }
699 // Remove from the database empty flows
700 if (empty)
701 conn.utils().removeFlowPath(conn, flowObj);
702 conn.endTx(Transaction.COMMIT);
703
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800704 return true;
705 }
706
707 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700708 * Clear the state for a previously added flow.
709 *
710 * @param flowId the Flow ID of the flow to clear.
711 * @return true on success, otherwise false.
712 */
713 @Override
714 public boolean clearFlow(FlowId flowId) {
715 IFlowPath flowObj = null;
716 try {
717 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
718 != null) {
719 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
720 flowId.toString());
721 } else {
722 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
723 flowId.toString());
724 }
725 } catch (Exception e) {
726 // TODO: handle exceptions
727 conn.endTx(Transaction.ROLLBACK);
728 log.error(":clearFlow FlowId:{} failed", flowId.toString());
729 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700730 if (flowObj == null) {
731 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700732 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700733 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700734
735 //
736 // Remove all Flow Entries
737 //
738 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
739 for (IFlowEntry flowEntryObj : flowEntries) {
740 flowObj.removeFlowEntry(flowEntryObj);
741 conn.utils().removeFlowEntry(conn, flowEntryObj);
742 }
743 // Remove the Flow itself
744 conn.utils().removeFlowPath(conn, flowObj);
745 conn.endTx(Transaction.COMMIT);
746
747 return true;
748 }
749
750 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800751 * Get a previously added flow.
752 *
753 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800754 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800755 */
756 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800757 public FlowPath getFlow(FlowId flowId) {
758 IFlowPath flowObj = null;
759 try {
760 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
761 != null) {
762 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
763 flowId.toString());
764 } else {
765 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
766 flowId.toString());
767 }
768 } catch (Exception e) {
769 // TODO: handle exceptions
770 conn.endTx(Transaction.ROLLBACK);
771 log.error(":getFlow FlowId:{} failed", flowId.toString());
772 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700773 if (flowObj == null) {
774 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800775 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700776 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800777
778 //
779 // Extract the Flow state
780 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800781 FlowPath flowPath = extractFlowPath(flowObj);
782 conn.endTx(Transaction.COMMIT);
783
784 return flowPath;
785 }
786
787 /**
788 * Get all previously added flows by a specific installer for a given
789 * data path endpoints.
790 *
791 * @param installerId the Caller ID of the installer of the flow to get.
792 * @param dataPathEndpoints the data path endpoints of the flow to get.
793 * @return the Flow Paths if found, otherwise null.
794 */
795 @Override
796 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
797 DataPathEndpoints dataPathEndpoints) {
798 //
799 // TODO: The implementation below is not optimal:
800 // We fetch all flows, and then return only the subset that match
801 // the query conditions.
802 // We should use the appropriate Titan/Gremlin query to filter-out
803 // the flows as appropriate.
804 //
805 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700806 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800807
808 if (allFlows == null) {
809 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700810 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800811 }
812
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800813 for (FlowPath flow : allFlows) {
814 //
815 // TODO: String-based comparison is sub-optimal.
816 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800817 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800818 //
819 if (! flow.installerId().toString().equals(installerId.toString()))
820 continue;
821 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
822 continue;
823 }
824 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
825 continue;
826 }
827 flowPaths.add(flow);
828 }
829
830 if (flowPaths.isEmpty()) {
831 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800832 } else {
833 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
834 }
835
836 return flowPaths;
837 }
838
839 /**
840 * Get all installed flows by all installers for given data path endpoints.
841 *
842 * @param dataPathEndpoints the data path endpoints of the flows to get.
843 * @return the Flow Paths if found, otherwise null.
844 */
845 @Override
846 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
847 //
848 // TODO: The implementation below is not optimal:
849 // We fetch all flows, and then return only the subset that match
850 // the query conditions.
851 // We should use the appropriate Titan/Gremlin query to filter-out
852 // the flows as appropriate.
853 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700854 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
855 ArrayList<FlowPath> allFlows = getAllFlows();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800856
857 if (allFlows == null) {
858 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700859 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800860 }
861
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800862 for (FlowPath flow : allFlows) {
863 //
864 // TODO: String-based comparison is sub-optimal.
865 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800866 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800867 //
868 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
869 continue;
870 }
871 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
872 continue;
873 }
874 flowPaths.add(flow);
875 }
876
877 if (flowPaths.isEmpty()) {
878 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800879 } else {
880 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
881 }
882
883 return flowPaths;
884 }
885
886 /**
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700887 * Get summary of all installed flows by all installers in a given range
888 *
889 * @param flowId the data path endpoints of the flows to get.
890 * @param maxFlows: the maximum number of flows to be returned
891 * @return the Flow Paths if found, otherwise null.
892 */
893 @Override
894 public ArrayList<FlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
895 //
896 // TODO: The implementation below is not optimal:
897 // We fetch all flows, and then return only the subset that match
898 // the query conditions.
899 // We should use the appropriate Titan/Gremlin query to filter-out
900 // the flows as appropriate.
901 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700902 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
903
904 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700905
906 if (allFlows == null) {
907 log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700908 return flowPaths;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700909 }
910
Umesh Krishnaswamy244b4ae2013-03-29 12:05:15 -0700911 Collections.sort(allFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700912
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700913 for (FlowPath flow : allFlows) {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700914 flow.setFlowEntryMatch(null);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700915
Pavlin Radoslavov96b43422013-04-04 19:14:56 -0700916 // start from desired flowId
917 if (flow.flowId().value() < flowId.value()) {
918 continue;
919 }
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700920
921 // Summarize by making null flow entry fields that are not relevant to report
922 for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
923 flowEntry.setFlowEntryActions(null);
924 flowEntry.setFlowEntryMatch(null);
925 }
926
927 flowPaths.add(flow);
928 if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
929 break;
930 }
931 }
932
933 if (flowPaths.isEmpty()) {
934 log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700935 } else {
936 log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
937 }
938
939 return flowPaths;
940 }
941
942 /**
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800943 * Get all installed flows by all installers.
944 *
945 * @return the Flow Paths if found, otherwise null.
946 */
947 @Override
948 public ArrayList<FlowPath> getAllFlows() {
949 Iterable<IFlowPath> flowPathsObj = null;
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700950 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800951
952 try {
953 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
954 log.debug("Get all FlowPaths: found FlowPaths");
955 } else {
956 log.debug("Get all FlowPaths: no FlowPaths found");
957 }
958 } catch (Exception e) {
959 // TODO: handle exceptions
960 conn.endTx(Transaction.ROLLBACK);
961 log.error(":getAllFlowPaths failed");
962 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700963 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
964 conn.endTx(Transaction.COMMIT);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700965 return flowPaths; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700966 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800967
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800968 for (IFlowPath flowObj : flowPathsObj) {
969 //
970 // Extract the Flow state
971 //
972 FlowPath flowPath = extractFlowPath(flowObj);
Pavlin Radoslavov3f2af732013-03-29 15:29:35 -0700973 if (flowPath != null)
974 flowPaths.add(flowPath);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800975 }
976
977 conn.endTx(Transaction.COMMIT);
978
979 return flowPaths;
980 }
981
982 /**
983 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
984 *
985 * @param flowObj the object to extract the Flow Path State from.
986 * @return the extracted Flow Path State.
987 */
988 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800989 //
990 // Extract the Flow state
991 //
Pavlin Radoslavovc2877682013-03-27 16:40:07 -0700992 String flowIdStr = flowObj.getFlowId();
993 String installerIdStr = flowObj.getInstallerId();
994 String srcSwitchStr = flowObj.getSrcSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700995 Short srcPortShort = flowObj.getSrcPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -0700996 String dstSwitchStr = flowObj.getDstSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700997 Short dstPortShort = flowObj.getDstPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -0700998
999 if ((flowIdStr == null) ||
1000 (installerIdStr == null) ||
1001 (srcSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001002 (srcPortShort == null) ||
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001003 (dstSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001004 (dstPortShort == null)) {
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001005 // TODO: A work-around, becauuse of some bogus database objects
1006 return null;
1007 }
1008
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001009 FlowPath flowPath = new FlowPath();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001010 flowPath.setFlowId(new FlowId(flowIdStr));
1011 flowPath.setInstallerId(new CallerId(installerIdStr));
1012 flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001013 flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001014 flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001015 flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001016 //
1017 // Extract the match conditions common for all Flow Entries
1018 //
1019 {
1020 FlowEntryMatch match = new FlowEntryMatch();
1021 Short matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1022 if (matchEthernetFrameType != null)
1023 match.enableEthernetFrameType(matchEthernetFrameType);
1024 String matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1025 if (matchSrcIPv4Net != null)
1026 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1027 String matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1028 if (matchDstIPv4Net != null)
1029 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1030 String matchSrcMac = flowObj.getMatchSrcMac();
1031 if (matchSrcMac != null)
1032 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1033 String matchDstMac = flowObj.getMatchDstMac();
1034 if (matchDstMac != null)
1035 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1036 flowPath.setFlowEntryMatch(match);
1037 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001038
1039 //
1040 // Extract all Flow Entries
1041 //
1042 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1043 for (IFlowEntry flowEntryObj : flowEntries) {
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001044 FlowEntry flowEntry = extractFlowEntry(flowEntryObj);
1045 if (flowEntry == null)
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001046 continue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001047 flowPath.dataPath().flowEntries().add(flowEntry);
1048 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001049
1050 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001051 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001052
1053 /**
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001054 * Extract Flow Entry State from a Titan Database Object @ref IFlowEntry.
1055 *
1056 * @param flowEntryObj the object to extract the Flow Entry State from.
1057 * @return the extracted Flow Entry State.
1058 */
1059 private FlowEntry extractFlowEntry(IFlowEntry flowEntryObj) {
1060 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1061 String switchDpidStr = flowEntryObj.getSwitchDpid();
1062 String userState = flowEntryObj.getUserState();
1063 String switchState = flowEntryObj.getSwitchState();
1064
1065 if ((flowEntryIdStr == null) ||
1066 (switchDpidStr == null) ||
1067 (userState == null) ||
1068 (switchState == null)) {
1069 // TODO: A work-around, becauuse of some bogus database objects
1070 return null;
1071 }
1072
1073 FlowEntry flowEntry = new FlowEntry();
1074 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
1075 flowEntry.setDpid(new Dpid(switchDpidStr));
1076
1077 //
1078 // Extract the match conditions
1079 //
1080 FlowEntryMatch match = new FlowEntryMatch();
1081 Short matchInPort = flowEntryObj.getMatchInPort();
1082 if (matchInPort != null)
1083 match.enableInPort(new Port(matchInPort));
1084 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1085 if (matchEthernetFrameType != null)
1086 match.enableEthernetFrameType(matchEthernetFrameType);
1087 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1088 if (matchSrcIPv4Net != null)
1089 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1090 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1091 if (matchDstIPv4Net != null)
1092 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1093 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1094 if (matchSrcMac != null)
1095 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1096 String matchDstMac = flowEntryObj.getMatchDstMac();
1097 if (matchDstMac != null)
1098 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1099 flowEntry.setFlowEntryMatch(match);
1100
1101 //
1102 // Extract the actions
1103 //
1104 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
1105 Short actionOutputPort = flowEntryObj.getActionOutput();
1106 if (actionOutputPort != null) {
1107 FlowEntryAction action = new FlowEntryAction();
1108 action.setActionOutput(new Port(actionOutputPort));
1109 actions.add(action);
1110 }
1111 flowEntry.setFlowEntryActions(actions);
1112 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
1113 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
1114 //
1115 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
1116 // and FlowEntryErrorState.
1117 //
1118 return flowEntry;
1119 }
1120
1121 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001122 * Add and maintain a shortest-path flow.
1123 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001124 * NOTE: The Flow Path argument does NOT contain flow entries.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001125 *
1126 * @param flowPath the Flow Path with the endpoints and the match
1127 * conditions to install.
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001128 * @return the added shortest-path flow on success, otherwise null.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001129 */
1130 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001131 public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001132 String dataPathSummaryStr = null;
1133
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001134 //
1135 // Do the shortest path computation
1136 //
1137 DataPath dataPath =
1138 topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
1139 flowPath.dataPath().dstPort());
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001140 if (dataPath == null) {
1141 // We need the DataPath to populate the Network MAP
1142 dataPath = new DataPath();
1143 dataPath.setSrcPort(flowPath.dataPath().srcPort());
1144 dataPath.setDstPort(flowPath.dataPath().dstPort());
1145 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001146
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001147 // Compute the Data Path summary
1148 dataPathSummaryStr = dataPath.dataPathSummary();
1149
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001150 //
1151 // Set the incoming port matching and the outgoing port output
1152 // actions for each flow entry.
1153 //
1154 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1155 // Set the incoming port matching
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001156 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001157 flowEntry.setFlowEntryMatch(flowEntryMatch);
1158 flowEntryMatch.enableInPort(flowEntry.inPort());
1159
1160 // Set the outgoing port output action
1161 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1162 if (flowEntryActions == null) {
1163 flowEntryActions = new ArrayList<FlowEntryAction>();
1164 flowEntry.setFlowEntryActions(flowEntryActions);
1165 }
1166 FlowEntryAction flowEntryAction = new FlowEntryAction();
1167 flowEntryAction.setActionOutput(flowEntry.outPort());
1168 flowEntryActions.add(flowEntryAction);
1169 }
1170
1171 //
1172 // Prepare the computed Flow Path
1173 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001174 FlowPath computedFlowPath = new FlowPath();
1175 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1176 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1177 computedFlowPath.setDataPath(dataPath);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001178 computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001179
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001180 FlowId flowId = new FlowId();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001181 if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001182 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001183
1184 // TODO: Mark the flow for maintenance purpose
1185
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001186 return (computedFlowPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001187 }
1188
1189 /**
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001190 * Reconcile all flows in a set.
1191 *
1192 * @param flowObjSet the set of flows that need to be reconciliated.
1193 */
1194 public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
1195 if (! flowObjSet.iterator().hasNext())
1196 return;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001197
1198 //
1199 // Remove the old Flow Entries, and add the new Flow Entries
1200 //
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001201
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001202 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001203 LinkedList<FlowPath> flowPaths = new LinkedList<FlowPath>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001204 for (IFlowPath flowObj : flowObjSet) {
1205 FlowPath flowPath = extractFlowPath(flowObj);
1206 if (flowPath == null)
1207 continue;
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001208 flowPaths.add(flowPath);
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001209
1210 //
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001211 // Remove the Flow Entries from the Network MAP
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001212 //
1213 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001214 LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001215 for (IFlowEntry flowEntryObj : flowEntries) {
1216 String dpidStr = flowEntryObj.getSwitchDpid();
1217 if (dpidStr == null)
1218 continue;
1219 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001220 /*
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001221 IOFSwitch mySwitch = mySwitches.get(dpid.value());
1222 if (mySwitch == null)
1223 continue; // Ignore the entry: not my switch
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001224 */
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001225 deleteFlowEntries.add(flowEntryObj);
1226 }
1227 for (IFlowEntry flowEntryObj : deleteFlowEntries) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001228 flowObj.removeFlowEntry(flowEntryObj);
1229 conn.utils().removeFlowEntry(conn, flowEntryObj);
1230 }
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001231 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001232
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001233 for (FlowPath flowPath : flowPaths) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001234 //
1235 // Delete the flow entries from the switches
1236 //
1237 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
1238 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001239 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1240 if (mySwitch == null) {
1241 // Not my switch
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001242 installRemoteFlowEntry(flowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001243 } else {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001244 installFlowEntry(mySwitch, flowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001245 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001246 }
1247
1248 //
1249 // Calculate the new shortest path and install it in the
1250 // Network MAP.
1251 //
1252 FlowPath addedFlowPath = addAndMaintainShortestPathFlow(flowPath);
1253 if (addedFlowPath == null) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001254 log.error("Cannot add Shortest Path Flow from {} to {}: path not found?",
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001255 flowPath.dataPath().srcPort().toString(),
1256 flowPath.dataPath().dstPort().toString());
1257 continue;
1258 }
1259
1260 //
1261 // Add the flow entries to the switches
1262 //
1263 for (FlowEntry flowEntry : addedFlowPath.dataPath().flowEntries()) {
1264 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001265 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1266 if (mySwitch == null) {
1267 // Not my switch
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001268 installRemoteFlowEntry(addedFlowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001269 continue;
1270 }
1271
1272 IFlowEntry flowEntryObj =
1273 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId());
1274 if (flowEntryObj == null) {
1275 //
1276 // TODO: Remove the "new Object[] wrapper in the statement
1277 // below after the SLF4J logger is upgraded to
1278 // Version 1.7.5
1279 //
1280 log.error("Cannot add Flow Entry to switch {} for Path Flow from {} to {} : Flow Entry not in the Network MAP",
1281 new Object[] {
1282 flowEntry.dpid(),
1283 flowPath.dataPath().srcPort(),
1284 flowPath.dataPath().dstPort()
1285 });
1286 continue;
1287 }
1288 // Update the Flow Entry Switch State in the Network MAP
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001289 if (installFlowEntry(mySwitch, addedFlowPath, flowEntry)) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001290 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1291 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001292 }
1293 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001294 }
1295
1296 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001297 * Install a Flow Entry on a switch.
1298 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001299 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001300 * @param flowObj the flow path object for the flow entry to install.
1301 * @param flowEntryObj the flow entry object to install.
1302 * @return true on success, otherwise false.
1303 */
1304 public boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
1305 IFlowEntry flowEntryObj) {
1306 FlowEntryId flowEntryId =
1307 new FlowEntryId(flowEntryObj.getFlowEntryId());
1308 String userState = flowEntryObj.getUserState();
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001309
1310 //
1311 // Create the Open Flow Flow Modification Entry to push
1312 //
1313 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1314 .getMessage(OFType.FLOW_MOD);
1315 long cookie = flowEntryId.value();
1316
1317 short flowModCommand = OFFlowMod.OFPFC_ADD;
1318 if (userState.equals("FE_USER_ADD")) {
1319 flowModCommand = OFFlowMod.OFPFC_ADD;
1320 } else if (userState.equals("FE_USER_MODIFY")) {
1321 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1322 } else if (userState.equals("FE_USER_DELETE")) {
1323 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1324 } else {
1325 // Unknown user state. Ignore the entry
1326 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1327 flowEntryId.toString(), userState);
1328 return false;
1329 }
1330
1331 //
1332 // Fetch the match conditions.
1333 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001334 // NOTE: The Flow matching conditions common for all Flow Entries are
1335 // used ONLY if a Flow Entry does NOT have the corresponding matching
1336 // condition set.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001337 //
1338 OFMatch match = new OFMatch();
1339 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001340
1341 // Match the Incoming Port
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001342 Short matchInPort = flowEntryObj.getMatchInPort();
1343 if (matchInPort != null) {
1344 match.setInputPort(matchInPort);
1345 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1346 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001347
1348 // Match the Ethernet Frame Type
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001349 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1350 if (matchEthernetFrameType == null)
1351 matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1352 if (matchEthernetFrameType != null) {
1353 match.setDataLayerType(matchEthernetFrameType);
1354 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1355 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001356
1357 // Match the Source IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001358 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1359 if (matchSrcIPv4Net == null)
1360 matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1361 if (matchSrcIPv4Net != null) {
1362 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
1363 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001364
1365 // Natch the Destination IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001366 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1367 if (matchDstIPv4Net == null)
1368 matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1369 if (matchDstIPv4Net != null) {
1370 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
1371 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001372
1373 // Match the Source MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001374 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1375 if (matchSrcMac == null)
1376 matchSrcMac = flowObj.getMatchSrcMac();
1377 if (matchSrcMac != null) {
1378 match.setDataLayerSource(matchSrcMac);
1379 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1380 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001381
1382 // Match the Destination MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001383 String matchDstMac = flowEntryObj.getMatchDstMac();
1384 if (matchDstMac == null)
1385 matchDstMac = flowObj.getMatchDstMac();
1386 if (matchDstMac != null) {
1387 match.setDataLayerDestination(matchDstMac);
1388 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1389 }
1390
1391 //
1392 // Fetch the actions
1393 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001394 // TODO: For now we support only the "OUTPUT" actions.
1395 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001396 List<OFAction> actions = new ArrayList<OFAction>();
1397 Short actionOutputPort = flowEntryObj.getActionOutput();
1398 if (actionOutputPort != null) {
1399 OFActionOutput action = new OFActionOutput();
1400 // XXX: The max length is hard-coded for now
1401 action.setMaxLength((short)0xffff);
1402 action.setPort(actionOutputPort);
1403 actions.add(action);
1404 }
1405
1406 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1407 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1408 .setPriority(PRIORITY_DEFAULT)
1409 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1410 .setCookie(cookie)
1411 .setCommand(flowModCommand)
1412 .setMatch(match)
1413 .setActions(actions)
1414 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1415 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1416 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1417 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1418 if (actionOutputPort != null)
1419 fm.setOutPort(actionOutputPort);
1420 }
1421
1422 //
1423 // TODO: Set the following flag
1424 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1425 // See method ForwardingBase::pushRoute()
1426 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001427
1428 //
1429 // Write the message to the switch
1430 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001431 try {
1432 messageDamper.write(mySwitch, fm, null);
1433 mySwitch.flush();
1434 //
1435 // TODO: We should use the OpenFlow Barrier mechanism
1436 // to check for errors, and update the SwitchState
1437 // for a flow entry after the Barrier message is
1438 // is received.
1439 //
1440 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1441 } catch (IOException e) {
1442 log.error("Failure writing flow mod from network map", e);
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001443 return false;
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001444 }
1445
1446 return true;
1447 }
1448
1449 /**
1450 * Install a Flow Entry on a switch.
1451 *
1452 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001453 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001454 * @param flowEntry the flow entry to install.
1455 * @return true on success, otherwise false.
1456 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001457 public boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1458 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001459 //
1460 // Create the OpenFlow Flow Modification Entry to push
1461 //
1462 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1463 .getMessage(OFType.FLOW_MOD);
1464 long cookie = flowEntry.flowEntryId().value();
1465
1466 short flowModCommand = OFFlowMod.OFPFC_ADD;
1467 if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
1468 flowModCommand = OFFlowMod.OFPFC_ADD;
1469 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
1470 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1471 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
1472 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1473 } else {
1474 // Unknown user state. Ignore the entry
1475 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1476 flowEntry.flowEntryId().toString(),
1477 flowEntry.flowEntryUserState());
1478 return false;
1479 }
1480
1481 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001482 // Fetch the match conditions.
1483 //
1484 // NOTE: The Flow matching conditions common for all Flow Entries are
1485 // used ONLY if a Flow Entry does NOT have the corresponding matching
1486 // condition set.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001487 //
1488 OFMatch match = new OFMatch();
1489 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001490 FlowEntryMatch flowPathMatch = flowPath.flowEntryMatch();
1491 FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
1492
1493 // Match the Incoming Port
1494 Port matchInPort = flowEntryMatch.inPort();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001495 if (matchInPort != null) {
1496 match.setInputPort(matchInPort.value());
1497 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1498 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001499
1500 // Match the Ethernet Frame Type
1501 Short matchEthernetFrameType = flowEntryMatch.ethernetFrameType();
1502 if ((matchEthernetFrameType == null) && (flowPathMatch != null)) {
1503 matchEthernetFrameType = flowPathMatch.ethernetFrameType();
1504 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001505 if (matchEthernetFrameType != null) {
1506 match.setDataLayerType(matchEthernetFrameType);
1507 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1508 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001509
1510 // Match the Source IPv4 Network prefix
1511 IPv4Net matchSrcIPv4Net = flowEntryMatch.srcIPv4Net();
1512 if ((matchSrcIPv4Net == null) && (flowPathMatch != null)) {
1513 matchSrcIPv4Net = flowPathMatch.srcIPv4Net();
1514 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001515 if (matchSrcIPv4Net != null) {
1516 match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
1517 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001518
1519 // Natch the Destination IPv4 Network prefix
1520 IPv4Net matchDstIPv4Net = flowEntryMatch.dstIPv4Net();
1521 if ((matchDstIPv4Net == null) && (flowPathMatch != null)) {
1522 matchDstIPv4Net = flowPathMatch.dstIPv4Net();
1523 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001524 if (matchDstIPv4Net != null) {
1525 match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
1526 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001527
1528 // Match the Source MAC address
1529 MACAddress matchSrcMac = flowEntryMatch.srcMac();
1530 if ((matchSrcMac == null) && (flowPathMatch != null)) {
1531 matchSrcMac = flowPathMatch.srcMac();
1532 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001533 if (matchSrcMac != null) {
1534 match.setDataLayerSource(matchSrcMac.toString());
1535 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1536 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001537
1538 // Match the Destination MAC address
1539 MACAddress matchDstMac = flowEntryMatch.dstMac();
1540 if ((matchDstMac == null) && (flowPathMatch != null)) {
1541 matchDstMac = flowPathMatch.dstMac();
1542 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001543 if (matchDstMac != null) {
1544 match.setDataLayerDestination(matchDstMac.toString());
1545 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1546 }
1547
1548 //
1549 // Fetch the actions
1550 //
1551 // TODO: For now we support only the "OUTPUT" actions.
1552 //
1553 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1554 List<OFAction> actions = new ArrayList<OFAction>();
1555 ArrayList<FlowEntryAction> flowEntryActions =
1556 flowEntry.flowEntryActions();
1557 for (FlowEntryAction flowEntryAction : flowEntryActions) {
1558 FlowEntryAction.ActionOutput actionOutput =
1559 flowEntryAction.actionOutput();
1560 if (actionOutput != null) {
1561 short actionOutputPort = actionOutput.port().value();
1562 OFActionOutput action = new OFActionOutput();
1563 // XXX: The max length is hard-coded for now
1564 action.setMaxLength((short)0xffff);
1565 action.setPort(actionOutputPort);
1566 actions.add(action);
1567 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1568 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1569 fm.setOutPort(actionOutputPort);
1570 }
1571 }
1572 }
1573
1574 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1575 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1576 .setPriority(PRIORITY_DEFAULT)
1577 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1578 .setCookie(cookie)
1579 .setCommand(flowModCommand)
1580 .setMatch(match)
1581 .setActions(actions)
1582 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1583
1584 //
1585 // TODO: Set the following flag
1586 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1587 // See method ForwardingBase::pushRoute()
1588 //
1589
1590 //
1591 // Write the message to the switch
1592 //
1593 try {
1594 messageDamper.write(mySwitch, fm, null);
1595 mySwitch.flush();
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001596 //
1597 // TODO: We should use the OpenFlow Barrier mechanism
1598 // to check for errors, and update the SwitchState
1599 // for a flow entry after the Barrier message is
1600 // is received.
1601 //
1602 // TODO: The FlowEntry Object in Titan should be set
1603 // to FE_SWITCH_UPDATED.
1604 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001605 } catch (IOException e) {
1606 log.error("Failure writing flow mod from network map", e);
1607 return false;
1608 }
1609 return true;
1610 }
1611
1612 /**
1613 * Remove a Flow Entry from a switch.
1614 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001615 * @param mySwitch the switch to remove the Flow Entry from.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001616 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001617 * @param flowEntry the flow entry to remove.
1618 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001619 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001620 public boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1621 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001622 //
1623 // The installFlowEntry() method implements both installation
1624 // and removal of flow entries.
1625 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001626 return (installFlowEntry(mySwitch, flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001627 }
1628
1629 /**
1630 * Install a Flow Entry on a remote controller.
1631 *
1632 * TODO: We need it now: Jono
1633 * - For now it will make a REST call to the remote controller.
1634 * - Internally, it needs to know the name of the remote controller.
1635 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001636 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001637 * @param flowEntry the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001638 * @return true on success, otherwise false.
1639 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001640 public boolean installRemoteFlowEntry(FlowPath flowPath,
1641 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001642 // TODO: We need it now: Jono
1643 // - For now it will make a REST call to the remote controller.
1644 // - Internally, it needs to know the name of the remote controller.
1645 return true;
1646 }
1647
1648 /**
1649 * Remove a flow entry on a remote controller.
1650 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001651 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001652 * @param flowEntry the flow entry to remove.
1653 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001654 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001655 public boolean removeRemoteFlowEntry(FlowPath flowPath,
1656 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001657 //
1658 // The installRemoteFlowEntry() method implements both installation
1659 // and removal of flow entries.
1660 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001661 return (installRemoteFlowEntry(flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001662 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001663}