blob: 57c136c417686ad6e82a77013f86e05dd3f7056f [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 Radoslavov759772f2013-05-20 20:50:00 +00004import java.io.PrintWriter;
5import java.io.StringWriter;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08006import java.util.ArrayList;
7import java.util.Collection;
Jonathan Hartf5315fb2013-04-05 11:41:56 -07008import java.util.Collections;
9import java.util.Comparator;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080010import java.util.EnumSet;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080011import java.util.HashMap;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +000012import java.util.LinkedList;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080013import java.util.List;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080014import java.util.Map;
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +000015import java.util.Random;
Pavlin Radoslavov759772f2013-05-20 20:50:00 +000016import java.util.concurrent.ConcurrentLinkedQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080017import java.util.concurrent.Executors;
18import java.util.concurrent.ScheduledExecutorService;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080019import java.util.concurrent.TimeUnit;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080020
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080021import net.floodlightcontroller.core.IFloodlightProviderService;
22import net.floodlightcontroller.core.INetMapStorage;
23import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
24import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070025import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
Pankaj Berded0079742013-03-27 17:53:25 -070026import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070027import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080028import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080029import net.floodlightcontroller.core.module.FloodlightModuleContext;
30import net.floodlightcontroller.core.module.FloodlightModuleException;
31import net.floodlightcontroller.core.module.IFloodlightModule;
32import net.floodlightcontroller.core.module.IFloodlightService;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080033import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
34import net.floodlightcontroller.restserver.IRestApiService;
35import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080036import net.floodlightcontroller.util.DataPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080037import net.floodlightcontroller.util.DataPathEndpoints;
Jonathan Hart01f2d272013-04-04 20:03:46 -070038import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080039import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070040import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080041import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070042import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080043import net.floodlightcontroller.util.FlowEntrySwitchState;
44import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080045import net.floodlightcontroller.util.FlowId;
46import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070047import net.floodlightcontroller.util.IPv4Net;
48import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080049import net.floodlightcontroller.util.OFMessageDamper;
50import net.floodlightcontroller.util.Port;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070051import net.floodlightcontroller.util.SwitchPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080052import net.onrc.onos.util.GraphDBConnection;
53import net.onrc.onos.util.GraphDBConnection.Transaction;
54
55import org.openflow.protocol.OFFlowMod;
56import org.openflow.protocol.OFMatch;
57import org.openflow.protocol.OFPacketOut;
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070058import org.openflow.protocol.OFPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080059import org.openflow.protocol.OFType;
60import org.openflow.protocol.action.OFAction;
61import org.openflow.protocol.action.OFActionOutput;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080062import org.slf4j.Logger;
63import org.slf4j.LoggerFactory;
64
Jonathan Hartf5315fb2013-04-05 11:41:56 -070065
Pavlin Radoslavov5adf1522013-04-04 17:43:41 -070066public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080067
68 public GraphDBConnection conn;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080069
70 protected IRestApiService restApi;
Jonathan Hart50a94982013-04-10 14:49:51 -070071 protected volatile IFloodlightProviderService floodlightProvider;
72 protected volatile ITopoRouteService topoRouteService;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070073 protected FloodlightModuleContext context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080074
75 protected OFMessageDamper messageDamper;
76
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070077 //
78 // TODO: Values copied from elsewhere (class LearningSwitch).
79 // The local copy should go away!
80 //
81 protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
82 protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
83 public static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0; // infinity
84 public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
85 public static final short PRIORITY_DEFAULT = 100;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080086
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +000087 // Flow Entry ID generation state
88 private static Random randomGenerator = new Random();
89 private static int nextFlowEntryIdPrefix = 0;
90 private static int nextFlowEntryIdSuffix = 0;
91 private static long nextFlowEntryId = 0;
92
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -070093 // State for measurement purpose
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070094 private static long measurementFlowId = 100000;
95 private static String measurementFlowIdStr = "0x186a0"; // 100000
96 private long modifiedMeasurementFlowTime = 0;
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -070097 //
98 private LinkedList<FlowPath> measurementStoredPaths = new LinkedList<FlowPath>();
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -070099 private long measurementStartTimeProcessingPaths = 0;
100 private long measurementEndTimeProcessingPaths = 0;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700101
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800102 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800103 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
104
105 // The periodic task(s)
Jonathan Hart50a94982013-04-10 14:49:51 -0700106 private ScheduledExecutorService mapReaderScheduler;
107 private ScheduledExecutorService shortestPathReconcileScheduler;
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700108
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700109 final Runnable mapReader = new Runnable() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800110 public void run() {
Pavlin Radoslavova75caea2013-04-10 19:11:26 -0700111 try {
112 runImpl();
113 } catch (Exception e) {
114 log.debug("Exception processing All Flow Entries from the Network MAP: ", e);
115 conn.endTx(Transaction.ROLLBACK);
116 return;
117 }
118 }
119
120 private void runImpl() {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700121 long startTime = System.nanoTime();
122 int counterAllFlowEntries = 0;
123 int counterMyNotUpdatedFlowEntries = 0;
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700124
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800125 if (floodlightProvider == null) {
126 log.debug("FloodlightProvider service not found!");
127 return;
128 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000129 Map<Long, IOFSwitch> mySwitches =
130 floodlightProvider.getSwitches();
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700131 LinkedList<IFlowEntry> addFlowEntries =
132 new LinkedList<IFlowEntry>();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000133 LinkedList<IFlowEntry> deleteFlowEntries =
134 new LinkedList<IFlowEntry>();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700135
136 //
Pankaj Berdea2e14a92013-04-15 11:59:15 -0700137 // Fetch all Flow Entries which need to be updated and select only my Flow Entries
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700138 // that need to be updated into the switches.
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700139 //
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700140 boolean processed_measurement_flow = false;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000141 Iterable<IFlowEntry> allFlowEntries =
Pankaj Berded1c38592013-04-10 22:46:40 -0700142 conn.utils().getAllSwitchNotUpdatedFlowEntries(conn);
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700143 for (IFlowEntry flowEntryObj : allFlowEntries) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700144 counterAllFlowEntries++;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000145
Pavlin Radoslavov6db8c6e2013-04-08 00:14:07 +0000146 String dpidStr = flowEntryObj.getSwitchDpid();
147 if (dpidStr == null)
148 continue;
149 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800150 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000151 if (mySwitch == null)
152 continue; // Ignore the entry: not my switch
153
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700154 IFlowPath flowObj =
155 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
156 if (flowObj == null)
157 continue; // Should NOT happen
158 if (flowObj.getFlowId() == null)
159 continue; // Invalid entry
160
161 //
162 // NOTE: For now we process the DELETE before the ADD
163 // to cover the more common scenario.
164 // TODO: This is error prone and needs to be fixed!
165 //
Pavlin Radoslavov6db8c6e2013-04-08 00:14:07 +0000166 String userState = flowEntryObj.getUserState();
167 if (userState == null)
168 continue;
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700169 if (userState.equals("FE_USER_DELETE")) {
170 // An entry that needs to be deleted.
171 deleteFlowEntries.add(flowEntryObj);
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700172 installFlowEntry(mySwitch, flowObj, flowEntryObj);
173 } else {
174 addFlowEntries.add(flowEntryObj);
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700175 }
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700176 counterMyNotUpdatedFlowEntries++;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700177 // Code for measurement purpose
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700178 // TODO: Commented-out for now
179 /*
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700180 {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700181 if (flowObj.getFlowId().equals(measurementFlowIdStr)) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700182 processed_measurement_flow = true;
183 }
184 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700185 */
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700186 }
187
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700188 //
189 // Process the Flow Entries that need to be added
190 //
191 for (IFlowEntry flowEntryObj : addFlowEntries) {
192 IFlowPath flowObj =
193 conn.utils().getFlowPathByFlowEntry(conn,
194 flowEntryObj);
195 if (flowObj == null)
196 continue; // Should NOT happen
197 if (flowObj.getFlowId() == null)
198 continue; // Invalid entry
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700199
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700200 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700201 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000202 if (mySwitch == null)
203 continue; // Shouldn't happen
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700204 installFlowEntry(mySwitch, flowObj, flowEntryObj);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800205 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000206
207 //
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000208 // Delete all Flow Entries marked for deletion from the
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700209 // Network MAP.
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000210 //
211 // TODO: We should use the OpenFlow Barrier mechanism
212 // to check for errors, and delete the Flow Entries after the
213 // Barrier message is received.
214 //
215 while (! deleteFlowEntries.isEmpty()) {
216 IFlowEntry flowEntryObj = deleteFlowEntries.poll();
217 IFlowPath flowObj =
218 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
219 if (flowObj == null) {
220 log.debug("Did not find FlowPath to be deleted");
221 continue;
222 }
223 flowObj.removeFlowEntry(flowEntryObj);
224 conn.utils().removeFlowEntry(conn, flowEntryObj);
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000225 }
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700226
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700227 conn.endTx(Transaction.COMMIT);
228
229 if (processed_measurement_flow) {
230 long estimatedTime =
231 System.nanoTime() - modifiedMeasurementFlowTime;
232 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
233 (double)estimatedTime / 1000000000 + " sec";
234 log.debug(logMsg);
235 }
236
237 long estimatedTime = System.nanoTime() - startTime;
238 double rate = 0.0;
239 if (estimatedTime > 0)
240 rate = ((double)counterAllFlowEntries * 1000000000) / estimatedTime;
241 String logMsg = "MEASUREMENT: Processed AllFlowEntries: " +
242 counterAllFlowEntries + " MyNotUpdatedFlowEntries: " +
243 counterMyNotUpdatedFlowEntries + " in " +
244 (double)estimatedTime / 1000000000 + " sec: " +
245 rate + " paths/s";
246 log.debug(logMsg);
247 }
248 };
249
250 final Runnable shortestPathReconcile = new Runnable() {
251 public void run() {
Pavlin Radoslavova75caea2013-04-10 19:11:26 -0700252 try {
253 runImpl();
254 } catch (Exception e) {
255 log.debug("Exception processing All Flows from the Network MAP: ", e);
256 conn.endTx(Transaction.ROLLBACK);
257 return;
258 }
259 }
260
261 private void runImpl() {
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700262 long startTime = System.nanoTime();
263 int counterAllFlowPaths = 0;
264 int counterMyFlowPaths = 0;
265
266 if (floodlightProvider == null) {
267 log.debug("FloodlightProvider service not found!");
268 return;
269 }
270 Map<Long, IOFSwitch> mySwitches =
271 floodlightProvider.getSwitches();
272 LinkedList<IFlowPath> deleteFlows = new LinkedList<IFlowPath>();
273
274 boolean processed_measurement_flow = false;
275
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700276 //
277 // Fetch and recompute the Shortest Path for those
278 // Flow Paths this controller is responsible for.
279 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700280 topoRouteService.prepareShortestPathTopo();
281 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700282 for (IFlowPath flowPathObj : allFlowPaths) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700283 counterAllFlowPaths++;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700284 if (flowPathObj == null)
285 continue;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700286
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700287 String srcDpidStr = flowPathObj.getSrcSwitch();
Pavlin Radoslavov6db8c6e2013-04-08 00:14:07 +0000288 if (srcDpidStr == null)
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700289 continue;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700290 Dpid srcDpid = new Dpid(srcDpidStr);
Pavlin Radoslavov2659a0b2013-04-03 20:30:40 -0700291 //
292 // Use the source DPID as a heuristic to decide
293 // which controller is responsible for maintaining the
294 // shortest path.
295 // NOTE: This heuristic is error-prone: if the switch
296 // goes away and no controller is responsible for that
297 // switch, then the original Flow Path is not cleaned-up
298 //
299 IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
300 if (mySwitch == null)
301 continue; // Ignore: not my responsibility
302
Pavlin Radoslavov99d1b152013-04-09 22:57:33 -0700303 // Test the Data Path Summary string
304 String dataPathSummaryStr = flowPathObj.getDataPathSummary();
305 if (dataPathSummaryStr == null)
306 continue; // Could be invalid entry?
307 if (dataPathSummaryStr.isEmpty())
308 continue; // No need to maintain this flow
309
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000310 //
311 // Test whether we need to complete the Flow cleanup,
312 // if the Flow has been deleted by the user.
313 //
314 String flowUserState = flowPathObj.getUserState();
315 if ((flowUserState != null)
316 && flowUserState.equals("FE_USER_DELETE")) {
317 Iterable<IFlowEntry> flowEntries = flowPathObj.getFlowEntries();
318 boolean empty = true; // TODO: an ugly hack
319 for (IFlowEntry flowEntryObj : flowEntries) {
320 empty = false;
321 break;
322 }
323 if (empty)
324 deleteFlows.add(flowPathObj);
325 }
326
Pavlin Radoslavov6db8c6e2013-04-08 00:14:07 +0000327 // Fetch the fields needed to recompute the shortest path
328 Short srcPortShort = flowPathObj.getSrcPort();
329 String dstDpidStr = flowPathObj.getDstSwitch();
330 Short dstPortShort = flowPathObj.getDstPort();
331 if ((srcPortShort == null) ||
332 (dstDpidStr == null) ||
333 (dstPortShort == null)) {
334 continue;
335 }
336
337 Port srcPort = new Port(srcPortShort);
338 Dpid dstDpid = new Dpid(dstDpidStr);
339 Port dstPort = new Port(dstPortShort);
340 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
341 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
342
Pavlin Radoslavov2659a0b2013-04-03 20:30:40 -0700343 counterMyFlowPaths++;
344
Pavlin Radoslavov832aa652013-03-29 16:21:59 -0700345 //
346 // NOTE: Using here the regular getShortestPath() method
347 // won't work here, because that method calls internally
348 // "conn.endTx(Transaction.COMMIT)", and that will
349 // invalidate all handlers to the Titan database.
350 // If we want to experiment with calling here
351 // getShortestPath(), we need to refactor that code
352 // to avoid closing the transaction.
353 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700354 DataPath dataPath =
355 topoRouteService.getTopoShortestPath(srcSwitchPort,
356 dstSwitchPort);
Pavlin Radoslavov4a325822013-04-02 22:16:59 +0000357 if (dataPath == null) {
358 // We need the DataPath to compare the paths
359 dataPath = new DataPath();
360 dataPath.setSrcPort(srcSwitchPort);
361 dataPath.setDstPort(dstSwitchPort);
362 }
363
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700364 String newDataPathSummaryStr = dataPath.dataPathSummary();
365 if (dataPathSummaryStr.equals(newDataPathSummaryStr))
366 continue; // Nothing changed
367
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700368 reconcileFlow(flowPathObj, dataPath);
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700369 }
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000370
371 //
372 // Delete all leftover Flows marked for deletion from the
373 // Network MAP.
374 //
375 while (! deleteFlows.isEmpty()) {
376 IFlowPath flowPathObj = deleteFlows.poll();
377 conn.utils().removeFlowPath(conn, flowPathObj);
378 }
379
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700380 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700381
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800382 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700383
384 if (processed_measurement_flow) {
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700385 long estimatedTime =
386 System.nanoTime() - modifiedMeasurementFlowTime;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700387 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
388 (double)estimatedTime / 1000000000 + " sec";
389 log.debug(logMsg);
390 }
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700391
392 long estimatedTime = System.nanoTime() - startTime;
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700393 double rate = 0.0;
394 if (estimatedTime > 0)
395 rate = ((double)counterAllFlowPaths * 1000000000) / estimatedTime;
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700396 String logMsg = "MEASUREMENT: Processed AllFlowPaths: " +
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700397 counterAllFlowPaths + " MyFlowPaths: " +
398 counterMyFlowPaths + " in " +
399 (double)estimatedTime / 1000000000 + " sec: " +
400 rate + " paths/s";
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700401 log.debug(logMsg);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800402 }
403 };
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700404
Jonathan Hart50a94982013-04-10 14:49:51 -0700405 //final ScheduledFuture<?> mapReaderHandle =
406 //mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800407
Jonathan Hart50a94982013-04-10 14:49:51 -0700408 //final ScheduledFuture<?> shortestPathReconcileHandle =
409 //shortestPathReconcileScheduler.scheduleAtFixedRate(shortestPathReconcile, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700410
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800411 @Override
412 public void init(String conf) {
Jonathan Hart50a94982013-04-10 14:49:51 -0700413 conn = GraphDBConnection.getInstance(conf);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800414 }
415
416 public void finalize() {
417 close();
418 }
419
420 @Override
421 public void close() {
422 conn.close();
423 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800424
425 @Override
426 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
427 Collection<Class<? extends IFloodlightService>> l =
428 new ArrayList<Class<? extends IFloodlightService>>();
429 l.add(IFlowService.class);
430 return l;
431 }
432
433 @Override
434 public Map<Class<? extends IFloodlightService>, IFloodlightService>
435 getServiceImpls() {
436 Map<Class<? extends IFloodlightService>,
437 IFloodlightService> m =
438 new HashMap<Class<? extends IFloodlightService>,
439 IFloodlightService>();
440 m.put(IFlowService.class, this);
441 return m;
442 }
443
444 @Override
445 public Collection<Class<? extends IFloodlightService>>
446 getModuleDependencies() {
447 Collection<Class<? extends IFloodlightService>> l =
448 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800449 l.add(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700450 l.add(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800451 l.add(IRestApiService.class);
452 return l;
453 }
454
455 @Override
456 public void init(FloodlightModuleContext context)
457 throws FloodlightModuleException {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700458 this.context = context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800459 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700460 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800461 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800462 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
463 EnumSet.of(OFType.FLOW_MOD),
464 OFMESSAGE_DAMPER_TIMEOUT);
465 // TODO: An ugly hack!
466 String conf = "/tmp/cassandra.titan";
467 this.init(conf);
Jonathan Hart50a94982013-04-10 14:49:51 -0700468
469 mapReaderScheduler = Executors.newScheduledThreadPool(1);
470 shortestPathReconcileScheduler = Executors.newScheduledThreadPool(1);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800471 }
472
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -0700473 private synchronized long getNextFlowEntryId() {
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000474 //
475 // Generate the next Flow Entry ID.
476 // NOTE: For now, the higher 32 bits are random, and
477 // the lower 32 bits are sequential.
478 // In the future, we need a better allocation mechanism.
479 //
480 if ((nextFlowEntryIdSuffix & 0xffffffffL) == 0xffffffffL) {
481 nextFlowEntryIdPrefix = randomGenerator.nextInt();
482 nextFlowEntryIdSuffix = 0;
483 } else {
484 nextFlowEntryIdSuffix++;
485 }
486 long result = (long)nextFlowEntryIdPrefix << 32;
487 result = result | (0xffffffffL & nextFlowEntryIdSuffix);
488 return result;
489 }
490
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800491 @Override
492 public void startUp(FloodlightModuleContext context) {
Jonathan Hart50a94982013-04-10 14:49:51 -0700493 restApi.addRestletRoutable(new FlowWebRoutable());
494
495 // Initialize the Flow Entry ID generator
496 nextFlowEntryIdPrefix = randomGenerator.nextInt();
497
498 mapReaderScheduler.scheduleAtFixedRate(
Pankaj Berde55f121a2013-04-23 15:42:54 -0700499 mapReader, 1, 1, TimeUnit.SECONDS);
Jonathan Hart50a94982013-04-10 14:49:51 -0700500 shortestPathReconcileScheduler.scheduleAtFixedRate(
Pankaj Berdea2e14a92013-04-15 11:59:15 -0700501 shortestPathReconcile, 100, 100, TimeUnit.MILLISECONDS);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800502 }
503
504 /**
505 * Add a flow.
506 *
507 * Internally, ONOS will automatically register the installer for
508 * receiving Flow Path Notifications for that path.
509 *
510 * @param flowPath the Flow Path to install.
511 * @param flowId the return-by-reference Flow ID as assigned internally.
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700512 * @param dataPathSummaryStr the data path summary string if the added
513 * flow will be maintained internally, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800514 * @return true on success, otherwise false.
515 */
516 @Override
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700517 public boolean addFlow(FlowPath flowPath, FlowId flowId,
518 String dataPathSummaryStr) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700519 /*
520 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700521 if (flowPath.flowId().value() == measurementFlowId) {
522 modifiedMeasurementFlowTime = System.nanoTime();
523 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700524 */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800525
526 IFlowPath flowObj = null;
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000527 boolean found = false;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800528 try {
529 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
530 != null) {
531 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
532 flowPath.flowId().toString());
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000533 found = true;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800534 } else {
535 flowObj = conn.utils().newFlowPath(conn);
536 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
537 flowPath.flowId().toString());
538 }
539 } catch (Exception e) {
540 // TODO: handle exceptions
541 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000542
543 StringWriter sw = new StringWriter();
544 e.printStackTrace(new PrintWriter(sw));
545 String stacktrace = sw.toString();
546
547 log.error(":addFlow FlowId:{} failed: {}",
548 flowPath.flowId().toString(),
549 stacktrace);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800550 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700551 if (flowObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000552 log.error(":addFlow FlowId:{} failed: Flow object not created",
553 flowPath.flowId().toString());
554 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800555 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700556 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800557
558 //
559 // Set the Flow key:
560 // - flowId
561 //
562 flowObj.setFlowId(flowPath.flowId().toString());
563 flowObj.setType("flow");
564
565 //
566 // Set the Flow attributes:
567 // - flowPath.installerId()
568 // - flowPath.dataPath().srcPort()
569 // - flowPath.dataPath().dstPort()
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700570 // - flowPath.matchEthernetFrameType()
571 // - flowPath.matchSrcIPv4Net()
572 // - flowPath.matchDstIPv4Net()
573 // - flowPath.matchSrcMac()
574 // - flowPath.matchDstMac()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800575 //
576 flowObj.setInstallerId(flowPath.installerId().toString());
577 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
578 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
579 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
580 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700581 if (flowPath.flowEntryMatch().matchEthernetFrameType()) {
582 flowObj.setMatchEthernetFrameType(flowPath.flowEntryMatch().ethernetFrameType());
583 }
584 if (flowPath.flowEntryMatch().matchSrcIPv4Net()) {
585 flowObj.setMatchSrcIPv4Net(flowPath.flowEntryMatch().srcIPv4Net().toString());
586 }
587 if (flowPath.flowEntryMatch().matchDstIPv4Net()) {
588 flowObj.setMatchDstIPv4Net(flowPath.flowEntryMatch().dstIPv4Net().toString());
589 }
590 if (flowPath.flowEntryMatch().matchSrcMac()) {
591 flowObj.setMatchSrcMac(flowPath.flowEntryMatch().srcMac().toString());
592 }
593 if (flowPath.flowEntryMatch().matchDstMac()) {
594 flowObj.setMatchDstMac(flowPath.flowEntryMatch().dstMac().toString());
595 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800596
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700597 if (dataPathSummaryStr != null) {
598 flowObj.setDataPathSummary(dataPathSummaryStr);
599 } else {
600 flowObj.setDataPathSummary("");
601 }
602
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000603 if (found)
604 flowObj.setUserState("FE_USER_MODIFY");
605 else
606 flowObj.setUserState("FE_USER_ADD");
607
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800608 // Flow edges:
609 // HeadFE
610
611
612 //
613 // Flow Entries:
614 // flowPath.dataPath().flowEntries()
615 //
616 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700617 if (addFlowEntry(flowObj, flowEntry) == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000618 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800619 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700620 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800621 }
622 conn.endTx(Transaction.COMMIT);
623
624 //
625 // TODO: We need a proper Flow ID allocation mechanism.
626 //
627 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700628
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800629 return true;
630 }
631
632 /**
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700633 * Add a flow entry to the Network MAP.
634 *
635 * @param flowObj the corresponding Flow Path object for the Flow Entry.
636 * @param flowEntry the Flow Entry to install.
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700637 * @return the added Flow Entry object on success, otherwise null.
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700638 */
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700639 private IFlowEntry addFlowEntry(IFlowPath flowObj, FlowEntry flowEntry) {
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700640 // Flow edges
641 // HeadFE (TODO)
642
643 //
644 // Assign the FlowEntry ID.
645 //
646 if ((flowEntry.flowEntryId() == null) ||
647 (flowEntry.flowEntryId().value() == 0)) {
648 long id = getNextFlowEntryId();
649 flowEntry.setFlowEntryId(new FlowEntryId(id));
650 }
651
652 IFlowEntry flowEntryObj = null;
653 boolean found = false;
654 try {
655 if ((flowEntryObj =
656 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
657 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
658 flowEntry.flowEntryId().toString());
659 found = true;
660 } else {
661 flowEntryObj = conn.utils().newFlowEntry(conn);
662 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
663 flowEntry.flowEntryId().toString());
664 }
665 } catch (Exception e) {
666 log.error(":addFlow FlowEntryId:{} failed",
667 flowEntry.flowEntryId().toString());
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700668 return null;
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700669 }
670 if (flowEntryObj == null) {
671 log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created",
672 flowEntry.flowEntryId().toString());
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700673 return null;
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700674 }
675
676 //
677 // Set the Flow Entry key:
678 // - flowEntry.flowEntryId()
679 //
680 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
681 flowEntryObj.setType("flow_entry");
682
683 //
684 // Set the Flow Entry Edges and attributes:
685 // - Switch edge
686 // - InPort edge
687 // - OutPort edge
688 //
689 // - flowEntry.flowEntryMatch()
690 // - flowEntry.flowEntryActions()
691 // - flowEntry.dpid()
692 // - flowEntry.flowEntryUserState()
693 // - flowEntry.flowEntrySwitchState()
694 // - flowEntry.flowEntryErrorState()
695 // - flowEntry.matchInPort()
696 // - flowEntry.matchEthernetFrameType()
697 // - flowEntry.matchSrcIPv4Net()
698 // - flowEntry.matchDstIPv4Net()
699 // - flowEntry.matchSrcMac()
700 // - flowEntry.matchDstMac()
701 // - flowEntry.actionOutput()
702 //
703 ISwitchObject sw =
704 conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
705 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
706 flowEntryObj.setSwitch(sw);
707 if (flowEntry.flowEntryMatch().matchInPort()) {
708 IPortObject inport =
709 conn.utils().searchPort(conn, flowEntry.dpid().toString(),
710 flowEntry.flowEntryMatch().inPort().value());
711 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
712 flowEntryObj.setInPort(inport);
713 }
714 if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
715 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
716 }
717 if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
718 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
719 }
720 if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
721 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
722 }
723 if (flowEntry.flowEntryMatch().matchSrcMac()) {
724 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
725 }
726 if (flowEntry.flowEntryMatch().matchDstMac()) {
727 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
728 }
729
730 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
731 if (fa.actionOutput() != null) {
732 IPortObject outport =
733 conn.utils().searchPort(conn,
734 flowEntry.dpid().toString(),
735 fa.actionOutput().port().value());
736 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
737 flowEntryObj.setOutPort(outport);
738 }
739 }
740 // TODO: Hacks with hard-coded state names!
741 if (found)
742 flowEntryObj.setUserState("FE_USER_MODIFY");
743 else
744 flowEntryObj.setUserState("FE_USER_ADD");
745 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
746 //
747 // TODO: Take care of the FlowEntryErrorState.
748 //
749
750 // Flow Entries edges:
751 // Flow
752 // NextFE (TODO)
753 if (! found) {
754 flowObj.addFlowEntry(flowEntryObj);
755 flowEntryObj.setFlow(flowObj);
756 }
757
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700758 return flowEntryObj;
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700759 }
760
761 /**
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000762 * Delete all previously added flows.
763 *
764 * @return true on success, otherwise false.
765 */
766 @Override
767 public boolean deleteAllFlows() {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000768 List<Thread> threads = new LinkedList<Thread>();
769 final ConcurrentLinkedQueue<FlowId> concurrentAllFlowIds =
770 new ConcurrentLinkedQueue<FlowId>();
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000771
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000772 // Get all Flow IDs
773 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
774 for (IFlowPath flowPathObj : allFlowPaths) {
775 if (flowPathObj == null)
776 continue;
777 String flowIdStr = flowPathObj.getFlowId();
778 if (flowIdStr == null)
779 continue;
780 FlowId flowId = new FlowId(flowIdStr);
781 concurrentAllFlowIds.add(flowId);
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000782 }
783
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000784 // Delete all flows one-by-one
785 for (FlowId flowId : concurrentAllFlowIds)
786 deleteFlow(flowId);
787
788 /*
789 * TODO: A faster mechanism to delete the Flow Paths by using
790 * a number of threads. Commented-out for now.
791 */
792 /*
793 //
794 // Create the threads to delete the Flow Paths
795 //
796 for (int i = 0; i < 10; i++) {
797 Thread thread = new Thread(new Runnable() {
798 @Override
799 public void run() {
800 while (true) {
801 FlowId flowId = concurrentAllFlowIds.poll();
802 if (flowId == null)
803 return;
804 deleteFlow(flowId);
805 }
806 }}, "Delete All Flow Paths");
807 threads.add(thread);
808 }
809
810 // Start processing
811 for (Thread thread : threads) {
812 thread.start();
813 }
814
815 // Want for all threads to complete
816 for (Thread thread : threads) {
817 try {
818 thread.join();
819 } catch (InterruptedException e) {
820 log.debug("Exception waiting for a thread to delete a Flow Path: ", e);
821 }
822 }
823 */
824
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000825 return true;
826 }
827
828 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800829 * Delete a previously added flow.
830 *
831 * @param flowId the Flow ID of the flow to delete.
832 * @return true on success, otherwise false.
833 */
834 @Override
835 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700836 /*
837 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700838 if (flowId.value() == measurementFlowId) {
839 modifiedMeasurementFlowTime = System.nanoTime();
840 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700841 */
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700842
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800843 IFlowPath flowObj = null;
844 //
845 // We just mark the entries for deletion,
846 // and let the switches remove each individual entry after
847 // it has been removed from the switches.
848 //
849 try {
850 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
851 != null) {
852 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
853 flowId.toString());
854 } else {
855 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
856 flowId.toString());
857 }
858 } catch (Exception e) {
859 // TODO: handle exceptions
860 conn.endTx(Transaction.ROLLBACK);
861 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
862 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700863 if (flowObj == null) {
864 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800865 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700866 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800867
868 //
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000869 // Find and mark for deletion all Flow Entries,
870 // and the Flow itself.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800871 //
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000872 flowObj.setUserState("FE_USER_DELETE");
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800873 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
874 boolean empty = true; // TODO: an ugly hack
875 for (IFlowEntry flowEntryObj : flowEntries) {
876 empty = false;
877 // flowObj.removeFlowEntry(flowEntryObj);
878 // conn.utils().removeFlowEntry(conn, flowEntryObj);
879 flowEntryObj.setUserState("FE_USER_DELETE");
880 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
881 }
882 // Remove from the database empty flows
883 if (empty)
884 conn.utils().removeFlowPath(conn, flowObj);
885 conn.endTx(Transaction.COMMIT);
886
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800887 return true;
888 }
889
890 /**
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000891 * Clear the state for all previously added flows.
892 *
893 * @return true on success, otherwise false.
894 */
895 @Override
896 public boolean clearAllFlows() {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000897 List<FlowId> allFlowIds = new LinkedList<FlowId>();
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000898
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000899 // Get all Flow IDs
900 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
901 for (IFlowPath flowPathObj : allFlowPaths) {
902 if (flowPathObj == null)
903 continue;
904 String flowIdStr = flowPathObj.getFlowId();
905 if (flowIdStr == null)
906 continue;
907 FlowId flowId = new FlowId(flowIdStr);
908 allFlowIds.add(flowId);
909 }
910
911 // Clear all flows one-by-one
912 for (FlowId flowId : allFlowIds) {
913 clearFlow(flowId);
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000914 }
915
916 return true;
917 }
918
919 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700920 * Clear the state for a previously added flow.
921 *
922 * @param flowId the Flow ID of the flow to clear.
923 * @return true on success, otherwise false.
924 */
925 @Override
926 public boolean clearFlow(FlowId flowId) {
927 IFlowPath flowObj = null;
928 try {
929 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
930 != null) {
931 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
932 flowId.toString());
933 } else {
934 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
935 flowId.toString());
936 }
937 } catch (Exception e) {
938 // TODO: handle exceptions
939 conn.endTx(Transaction.ROLLBACK);
940 log.error(":clearFlow FlowId:{} failed", flowId.toString());
941 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700942 if (flowObj == null) {
943 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700944 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700945 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700946
947 //
948 // Remove all Flow Entries
949 //
950 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
951 for (IFlowEntry flowEntryObj : flowEntries) {
952 flowObj.removeFlowEntry(flowEntryObj);
953 conn.utils().removeFlowEntry(conn, flowEntryObj);
954 }
955 // Remove the Flow itself
956 conn.utils().removeFlowPath(conn, flowObj);
957 conn.endTx(Transaction.COMMIT);
958
959 return true;
960 }
961
962 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800963 * Get a previously added flow.
964 *
965 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800966 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800967 */
968 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800969 public FlowPath getFlow(FlowId flowId) {
970 IFlowPath flowObj = null;
971 try {
972 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
973 != null) {
974 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
975 flowId.toString());
976 } else {
977 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
978 flowId.toString());
979 }
980 } catch (Exception e) {
981 // TODO: handle exceptions
982 conn.endTx(Transaction.ROLLBACK);
983 log.error(":getFlow FlowId:{} failed", flowId.toString());
984 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700985 if (flowObj == null) {
986 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800987 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700988 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800989
990 //
991 // Extract the Flow state
992 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800993 FlowPath flowPath = extractFlowPath(flowObj);
994 conn.endTx(Transaction.COMMIT);
995
996 return flowPath;
997 }
998
999 /**
1000 * Get all previously added flows by a specific installer for a given
1001 * data path endpoints.
1002 *
1003 * @param installerId the Caller ID of the installer of the flow to get.
1004 * @param dataPathEndpoints the data path endpoints of the flow to get.
1005 * @return the Flow Paths if found, otherwise null.
1006 */
1007 @Override
1008 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
1009 DataPathEndpoints dataPathEndpoints) {
1010 //
1011 // TODO: The implementation below is not optimal:
1012 // We fetch all flows, and then return only the subset that match
1013 // the query conditions.
1014 // We should use the appropriate Titan/Gremlin query to filter-out
1015 // the flows as appropriate.
1016 //
1017 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001018 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001019
1020 if (allFlows == null) {
1021 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001022 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001023 }
1024
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001025 for (FlowPath flow : allFlows) {
1026 //
1027 // TODO: String-based comparison is sub-optimal.
1028 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -08001029 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001030 //
1031 if (! flow.installerId().toString().equals(installerId.toString()))
1032 continue;
1033 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
1034 continue;
1035 }
1036 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
1037 continue;
1038 }
1039 flowPaths.add(flow);
1040 }
1041
1042 if (flowPaths.isEmpty()) {
1043 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001044 } else {
1045 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
1046 }
1047
1048 return flowPaths;
1049 }
1050
1051 /**
1052 * Get all installed flows by all installers for given data path endpoints.
1053 *
1054 * @param dataPathEndpoints the data path endpoints of the flows to get.
1055 * @return the Flow Paths if found, otherwise null.
1056 */
1057 @Override
1058 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
1059 //
1060 // TODO: The implementation below is not optimal:
1061 // We fetch all flows, and then return only the subset that match
1062 // the query conditions.
1063 // We should use the appropriate Titan/Gremlin query to filter-out
1064 // the flows as appropriate.
1065 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001066 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1067 ArrayList<FlowPath> allFlows = getAllFlows();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001068
1069 if (allFlows == null) {
1070 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001071 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001072 }
1073
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001074 for (FlowPath flow : allFlows) {
1075 //
1076 // TODO: String-based comparison is sub-optimal.
1077 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -08001078 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001079 //
1080 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
1081 continue;
1082 }
1083 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
1084 continue;
1085 }
1086 flowPaths.add(flow);
1087 }
1088
1089 if (flowPaths.isEmpty()) {
1090 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001091 } else {
1092 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
1093 }
1094
1095 return flowPaths;
1096 }
1097
1098 /**
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001099 * Get summary of all installed flows by all installers in a given range
1100 *
1101 * @param flowId the data path endpoints of the flows to get.
1102 * @param maxFlows: the maximum number of flows to be returned
1103 * @return the Flow Paths if found, otherwise null.
1104 */
1105 @Override
Jonathan Hart01f2d272013-04-04 20:03:46 -07001106 public ArrayList<IFlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
Jonathan Hartf5315fb2013-04-05 11:41:56 -07001107
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001108 // TODO: The implementation below is not optimal:
1109 // We fetch all flows, and then return only the subset that match
1110 // the query conditions.
1111 // We should use the appropriate Titan/Gremlin query to filter-out
1112 // the flows as appropriate.
1113 //
Jonathan Hart01f2d272013-04-04 20:03:46 -07001114 //ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001115
Jonathan Hart01f2d272013-04-04 20:03:46 -07001116 ArrayList<IFlowPath> flowPathsWithoutFlowEntries = getAllFlowsWithoutFlowEntries();
1117
Jonathan Hartf5315fb2013-04-05 11:41:56 -07001118 Collections.sort(flowPathsWithoutFlowEntries,
1119 new Comparator<IFlowPath>(){
1120 @Override
1121 public int compare(IFlowPath first, IFlowPath second) {
1122 // TODO Auto-generated method stub
1123 long result = new FlowId(first.getFlowId()).value()
1124 - new FlowId(second.getFlowId()).value();
1125 if (result > 0) return 1;
1126 else if (result < 0) return -1;
1127 else return 0;
1128 }
1129 }
1130 );
1131
Jonathan Hart01f2d272013-04-04 20:03:46 -07001132 return flowPathsWithoutFlowEntries;
1133
1134 /*
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001135 ArrayList<FlowPath> allFlows = getAllFlows();
Jonathan Hart01f2d272013-04-04 20:03:46 -07001136
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001137 if (allFlows == null) {
1138 log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001139 return flowPaths;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001140 }
1141
Umesh Krishnaswamy244b4ae2013-03-29 12:05:15 -07001142 Collections.sort(allFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001143
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001144 for (FlowPath flow : allFlows) {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001145 flow.setFlowEntryMatch(null);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001146
Pavlin Radoslavov96b43422013-04-04 19:14:56 -07001147 // start from desired flowId
1148 if (flow.flowId().value() < flowId.value()) {
1149 continue;
1150 }
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001151
1152 // Summarize by making null flow entry fields that are not relevant to report
1153 for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
1154 flowEntry.setFlowEntryActions(null);
1155 flowEntry.setFlowEntryMatch(null);
1156 }
1157
1158 flowPaths.add(flow);
1159 if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
1160 break;
1161 }
1162 }
1163
1164 if (flowPaths.isEmpty()) {
1165 log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001166 } else {
1167 log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
1168 }
1169
1170 return flowPaths;
Jonathan Hart01f2d272013-04-04 20:03:46 -07001171 */
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001172 }
1173
1174 /**
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001175 * Get all installed flows by all installers.
1176 *
1177 * @return the Flow Paths if found, otherwise null.
1178 */
1179 @Override
1180 public ArrayList<FlowPath> getAllFlows() {
1181 Iterable<IFlowPath> flowPathsObj = null;
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001182 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001183
1184 try {
1185 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
1186 log.debug("Get all FlowPaths: found FlowPaths");
1187 } else {
1188 log.debug("Get all FlowPaths: no FlowPaths found");
1189 }
1190 } catch (Exception e) {
1191 // TODO: handle exceptions
1192 conn.endTx(Transaction.ROLLBACK);
1193 log.error(":getAllFlowPaths failed");
1194 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001195 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
1196 conn.endTx(Transaction.COMMIT);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001197 return flowPaths; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001198 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001199
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001200 for (IFlowPath flowObj : flowPathsObj) {
1201 //
1202 // Extract the Flow state
1203 //
1204 FlowPath flowPath = extractFlowPath(flowObj);
Pavlin Radoslavov3f2af732013-03-29 15:29:35 -07001205 if (flowPath != null)
1206 flowPaths.add(flowPath);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001207 }
1208
1209 conn.endTx(Transaction.COMMIT);
1210
1211 return flowPaths;
1212 }
Jonathan Hart01f2d272013-04-04 20:03:46 -07001213
1214 public ArrayList<IFlowPath> getAllFlowsWithoutFlowEntries(){
1215 Iterable<IFlowPath> flowPathsObj = null;
1216 ArrayList<IFlowPath> flowPathsObjArray = new ArrayList<IFlowPath>();
1217 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1218
Jonathan Harte6e91872013-04-13 11:10:32 -07001219 conn.endTx(Transaction.COMMIT);
1220
Jonathan Hart01f2d272013-04-04 20:03:46 -07001221 try {
1222 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
1223 log.debug("Get all FlowPaths: found FlowPaths");
1224 } else {
1225 log.debug("Get all FlowPaths: no FlowPaths found");
1226 }
1227 } catch (Exception e) {
1228 // TODO: handle exceptions
1229 conn.endTx(Transaction.ROLLBACK);
1230 log.error(":getAllFlowPaths failed");
1231 }
1232 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
1233 return new ArrayList<IFlowPath>(); // No Flows found
1234 }
1235
1236 for (IFlowPath flowObj : flowPathsObj){
1237 flowPathsObjArray.add(flowObj);
1238 }
1239 /*
1240 for (IFlowPath flowObj : flowPathsObj) {
1241 //
1242 // Extract the Flow state
1243 //
1244 FlowPath flowPath = extractFlowPath(flowObj);
1245 if (flowPath != null)
1246 flowPaths.add(flowPath);
1247 }
1248 */
1249
1250 //conn.endTx(Transaction.COMMIT);
1251
1252 return flowPathsObjArray;
1253 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001254
1255 /**
1256 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
1257 *
1258 * @param flowObj the object to extract the Flow Path State from.
1259 * @return the extracted Flow Path State.
1260 */
1261 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001262 //
1263 // Extract the Flow state
1264 //
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001265 String flowIdStr = flowObj.getFlowId();
1266 String installerIdStr = flowObj.getInstallerId();
1267 String srcSwitchStr = flowObj.getSrcSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001268 Short srcPortShort = flowObj.getSrcPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001269 String dstSwitchStr = flowObj.getDstSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001270 Short dstPortShort = flowObj.getDstPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001271
1272 if ((flowIdStr == null) ||
1273 (installerIdStr == null) ||
1274 (srcSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001275 (srcPortShort == null) ||
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001276 (dstSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001277 (dstPortShort == null)) {
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001278 // TODO: A work-around, becauuse of some bogus database objects
1279 return null;
1280 }
1281
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001282 FlowPath flowPath = new FlowPath();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001283 flowPath.setFlowId(new FlowId(flowIdStr));
1284 flowPath.setInstallerId(new CallerId(installerIdStr));
1285 flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001286 flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001287 flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001288 flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001289 //
1290 // Extract the match conditions common for all Flow Entries
1291 //
1292 {
1293 FlowEntryMatch match = new FlowEntryMatch();
1294 Short matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1295 if (matchEthernetFrameType != null)
1296 match.enableEthernetFrameType(matchEthernetFrameType);
1297 String matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1298 if (matchSrcIPv4Net != null)
1299 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1300 String matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1301 if (matchDstIPv4Net != null)
1302 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1303 String matchSrcMac = flowObj.getMatchSrcMac();
1304 if (matchSrcMac != null)
1305 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1306 String matchDstMac = flowObj.getMatchDstMac();
1307 if (matchDstMac != null)
1308 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1309 flowPath.setFlowEntryMatch(match);
1310 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001311
1312 //
1313 // Extract all Flow Entries
1314 //
1315 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1316 for (IFlowEntry flowEntryObj : flowEntries) {
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001317 FlowEntry flowEntry = extractFlowEntry(flowEntryObj);
1318 if (flowEntry == null)
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001319 continue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001320 flowPath.dataPath().flowEntries().add(flowEntry);
1321 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001322
1323 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001324 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001325
1326 /**
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001327 * Extract Flow Entry State from a Titan Database Object @ref IFlowEntry.
1328 *
1329 * @param flowEntryObj the object to extract the Flow Entry State from.
1330 * @return the extracted Flow Entry State.
1331 */
1332 private FlowEntry extractFlowEntry(IFlowEntry flowEntryObj) {
1333 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1334 String switchDpidStr = flowEntryObj.getSwitchDpid();
1335 String userState = flowEntryObj.getUserState();
1336 String switchState = flowEntryObj.getSwitchState();
1337
1338 if ((flowEntryIdStr == null) ||
1339 (switchDpidStr == null) ||
1340 (userState == null) ||
1341 (switchState == null)) {
1342 // TODO: A work-around, becauuse of some bogus database objects
1343 return null;
1344 }
1345
1346 FlowEntry flowEntry = new FlowEntry();
1347 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
1348 flowEntry.setDpid(new Dpid(switchDpidStr));
1349
1350 //
1351 // Extract the match conditions
1352 //
1353 FlowEntryMatch match = new FlowEntryMatch();
1354 Short matchInPort = flowEntryObj.getMatchInPort();
1355 if (matchInPort != null)
1356 match.enableInPort(new Port(matchInPort));
1357 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1358 if (matchEthernetFrameType != null)
1359 match.enableEthernetFrameType(matchEthernetFrameType);
1360 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1361 if (matchSrcIPv4Net != null)
1362 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1363 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1364 if (matchDstIPv4Net != null)
1365 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1366 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1367 if (matchSrcMac != null)
1368 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1369 String matchDstMac = flowEntryObj.getMatchDstMac();
1370 if (matchDstMac != null)
1371 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1372 flowEntry.setFlowEntryMatch(match);
1373
1374 //
1375 // Extract the actions
1376 //
1377 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
1378 Short actionOutputPort = flowEntryObj.getActionOutput();
1379 if (actionOutputPort != null) {
1380 FlowEntryAction action = new FlowEntryAction();
1381 action.setActionOutput(new Port(actionOutputPort));
1382 actions.add(action);
1383 }
1384 flowEntry.setFlowEntryActions(actions);
1385 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
1386 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
1387 //
1388 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
1389 // and FlowEntryErrorState.
1390 //
1391 return flowEntry;
1392 }
1393
1394 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001395 * Add and maintain a shortest-path flow.
1396 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001397 * NOTE: The Flow Path argument does NOT contain flow entries.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001398 *
1399 * @param flowPath the Flow Path with the endpoints and the match
1400 * conditions to install.
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001401 * @return the added shortest-path flow on success, otherwise null.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001402 */
1403 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001404 public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001405 //
Pavlin Radoslavov8b4b0592013-04-10 04:33:33 +00001406 // Don't do the shortest path computation here.
1407 // Instead, let the Flow reconciliation thread take care of it.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001408 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001409
Pavlin Radoslavov8b4b0592013-04-10 04:33:33 +00001410 // We need the DataPath to populate the Network MAP
1411 DataPath dataPath = new DataPath();
1412 dataPath.setSrcPort(flowPath.dataPath().srcPort());
1413 dataPath.setDstPort(flowPath.dataPath().dstPort());
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001414
1415 //
1416 // Prepare the computed Flow Path
1417 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001418 FlowPath computedFlowPath = new FlowPath();
1419 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1420 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1421 computedFlowPath.setDataPath(dataPath);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001422 computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001423
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001424 FlowId flowId = new FlowId();
Pavlin Radoslavov8b4b0592013-04-10 04:33:33 +00001425 String dataPathSummaryStr = dataPath.dataPathSummary();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001426 if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001427 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001428
1429 // TODO: Mark the flow for maintenance purpose
1430
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001431 return (computedFlowPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001432 }
1433
1434 /**
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001435 * Reconcile a flow.
1436 *
1437 * @param flowObj the flow that needs to be reconciliated.
1438 * @param newDataPath the new data path to use.
1439 * @return true on success, otherwise false.
1440 */
1441 public boolean reconcileFlow(IFlowPath flowObj, DataPath newDataPath) {
1442 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
1443
1444 //
1445 // Set the incoming port matching and the outgoing port output
1446 // actions for each flow entry.
1447 //
1448 for (FlowEntry flowEntry : newDataPath.flowEntries()) {
1449 // Set the incoming port matching
1450 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
1451 flowEntry.setFlowEntryMatch(flowEntryMatch);
1452 flowEntryMatch.enableInPort(flowEntry.inPort());
1453
1454 // Set the outgoing port output action
1455 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1456 if (flowEntryActions == null) {
1457 flowEntryActions = new ArrayList<FlowEntryAction>();
1458 flowEntry.setFlowEntryActions(flowEntryActions);
1459 }
1460 FlowEntryAction flowEntryAction = new FlowEntryAction();
1461 flowEntryAction.setActionOutput(flowEntry.outPort());
1462 flowEntryActions.add(flowEntryAction);
1463 }
1464
1465 //
1466 // Remove the old Flow Entries, and add the new Flow Entries
1467 //
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001468 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1469 LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
1470 for (IFlowEntry flowEntryObj : flowEntries) {
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001471 flowEntryObj.setUserState("FE_USER_DELETE");
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -07001472 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001473 }
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001474 for (FlowEntry flowEntry : newDataPath.flowEntries()) {
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -07001475 addFlowEntry(flowObj, flowEntry);
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001476 }
1477
1478 //
1479 // Set the Data Path Summary
1480 //
1481 String dataPathSummaryStr = newDataPath.dataPathSummary();
1482 flowObj.setDataPathSummary(dataPathSummaryStr);
1483
1484 return true;
1485 }
1486
1487 /**
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001488 * Reconcile all flows in a set.
1489 *
1490 * @param flowObjSet the set of flows that need to be reconciliated.
1491 */
1492 public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
1493 if (! flowObjSet.iterator().hasNext())
1494 return;
Pavlin Radoslavov0eeb15d2013-04-05 10:23:51 -07001495 // TODO: Not implemented/used yet.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001496 }
1497
1498 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001499 * Install a Flow Entry on a switch.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001500 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001501 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001502 * @param flowObj the flow path object for the flow entry to install.
1503 * @param flowEntryObj the flow entry object to install.
1504 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001505 */
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001506 public boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
1507 IFlowEntry flowEntryObj) {
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001508 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1509 if (flowEntryIdStr == null)
1510 return false;
1511 FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001512 String userState = flowEntryObj.getUserState();
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001513 if (userState == null)
1514 return false;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001515
1516 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001517 // Create the Open Flow Flow Modification Entry to push
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001518 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001519 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1520 .getMessage(OFType.FLOW_MOD);
1521 long cookie = flowEntryId.value();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001522
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001523 short flowModCommand = OFFlowMod.OFPFC_ADD;
1524 if (userState.equals("FE_USER_ADD")) {
1525 flowModCommand = OFFlowMod.OFPFC_ADD;
1526 } else if (userState.equals("FE_USER_MODIFY")) {
1527 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1528 } else if (userState.equals("FE_USER_DELETE")) {
1529 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1530 } else {
1531 // Unknown user state. Ignore the entry
1532 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1533 flowEntryId.toString(), userState);
1534 return false;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001535 }
1536
1537 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001538 // Fetch the match conditions.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001539 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001540 // NOTE: The Flow matching conditions common for all Flow Entries are
1541 // used ONLY if a Flow Entry does NOT have the corresponding matching
1542 // condition set.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001543 //
1544 OFMatch match = new OFMatch();
1545 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001546
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001547 // Match the Incoming Port
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001548 Short matchInPort = flowEntryObj.getMatchInPort();
1549 if (matchInPort != null) {
1550 match.setInputPort(matchInPort);
1551 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1552 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001553
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001554 // Match the Ethernet Frame Type
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001555 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1556 if (matchEthernetFrameType == null)
1557 matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1558 if (matchEthernetFrameType != null) {
1559 match.setDataLayerType(matchEthernetFrameType);
1560 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1561 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001562
1563 // Match the Source IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001564 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1565 if (matchSrcIPv4Net == null)
1566 matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1567 if (matchSrcIPv4Net != null) {
1568 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
1569 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001570
1571 // Natch the Destination IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001572 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1573 if (matchDstIPv4Net == null)
1574 matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1575 if (matchDstIPv4Net != null) {
1576 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
1577 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001578
1579 // Match the Source MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001580 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1581 if (matchSrcMac == null)
1582 matchSrcMac = flowObj.getMatchSrcMac();
1583 if (matchSrcMac != null) {
1584 match.setDataLayerSource(matchSrcMac);
1585 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1586 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001587
1588 // Match the Destination MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001589 String matchDstMac = flowEntryObj.getMatchDstMac();
1590 if (matchDstMac == null)
1591 matchDstMac = flowObj.getMatchDstMac();
1592 if (matchDstMac != null) {
1593 match.setDataLayerDestination(matchDstMac);
1594 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1595 }
1596
1597 //
1598 // Fetch the actions
1599 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001600 // TODO: For now we support only the "OUTPUT" actions.
1601 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001602 List<OFAction> actions = new ArrayList<OFAction>();
1603 Short actionOutputPort = flowEntryObj.getActionOutput();
1604 if (actionOutputPort != null) {
1605 OFActionOutput action = new OFActionOutput();
1606 // XXX: The max length is hard-coded for now
1607 action.setMaxLength((short)0xffff);
1608 action.setPort(actionOutputPort);
1609 actions.add(action);
1610 }
1611
1612 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1613 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1614 .setPriority(PRIORITY_DEFAULT)
1615 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1616 .setCookie(cookie)
1617 .setCommand(flowModCommand)
1618 .setMatch(match)
1619 .setActions(actions)
1620 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1621 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1622 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1623 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1624 if (actionOutputPort != null)
1625 fm.setOutPort(actionOutputPort);
1626 }
1627
1628 //
1629 // TODO: Set the following flag
1630 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1631 // See method ForwardingBase::pushRoute()
1632 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001633
1634 //
1635 // Write the message to the switch
1636 //
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001637 log.debug("MEASUREMENT: Installing flow entry " + userState +
1638 " into switch DPID: " +
1639 mySwitch.getStringId() +
1640 " flowEntryId: " + flowEntryId.toString() +
1641 " srcMac: " + matchSrcMac + " dstMac: " + matchDstMac +
1642 " inPort: " + matchInPort + " outPort: " + actionOutputPort
1643 );
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001644 try {
1645 messageDamper.write(mySwitch, fm, null);
1646 mySwitch.flush();
1647 //
1648 // TODO: We should use the OpenFlow Barrier mechanism
1649 // to check for errors, and update the SwitchState
1650 // for a flow entry after the Barrier message is
1651 // is received.
1652 //
1653 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1654 } catch (IOException e) {
1655 log.error("Failure writing flow mod from network map", e);
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001656 return false;
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001657 }
1658
1659 return true;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001660 }
1661
1662 /**
1663 * Install a Flow Entry on a switch.
1664 *
1665 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001666 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001667 * @param flowEntry the flow entry to install.
1668 * @return true on success, otherwise false.
1669 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001670 public boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1671 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001672 //
1673 // Create the OpenFlow Flow Modification Entry to push
1674 //
1675 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1676 .getMessage(OFType.FLOW_MOD);
1677 long cookie = flowEntry.flowEntryId().value();
1678
1679 short flowModCommand = OFFlowMod.OFPFC_ADD;
1680 if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
1681 flowModCommand = OFFlowMod.OFPFC_ADD;
1682 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
1683 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1684 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
1685 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1686 } else {
1687 // Unknown user state. Ignore the entry
1688 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1689 flowEntry.flowEntryId().toString(),
1690 flowEntry.flowEntryUserState());
1691 return false;
1692 }
1693
1694 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001695 // Fetch the match conditions.
1696 //
1697 // NOTE: The Flow matching conditions common for all Flow Entries are
1698 // used ONLY if a Flow Entry does NOT have the corresponding matching
1699 // condition set.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001700 //
1701 OFMatch match = new OFMatch();
1702 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001703 FlowEntryMatch flowPathMatch = flowPath.flowEntryMatch();
1704 FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
1705
1706 // Match the Incoming Port
1707 Port matchInPort = flowEntryMatch.inPort();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001708 if (matchInPort != null) {
1709 match.setInputPort(matchInPort.value());
1710 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1711 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001712
1713 // Match the Ethernet Frame Type
1714 Short matchEthernetFrameType = flowEntryMatch.ethernetFrameType();
1715 if ((matchEthernetFrameType == null) && (flowPathMatch != null)) {
1716 matchEthernetFrameType = flowPathMatch.ethernetFrameType();
1717 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001718 if (matchEthernetFrameType != null) {
1719 match.setDataLayerType(matchEthernetFrameType);
1720 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1721 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001722
1723 // Match the Source IPv4 Network prefix
1724 IPv4Net matchSrcIPv4Net = flowEntryMatch.srcIPv4Net();
1725 if ((matchSrcIPv4Net == null) && (flowPathMatch != null)) {
1726 matchSrcIPv4Net = flowPathMatch.srcIPv4Net();
1727 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001728 if (matchSrcIPv4Net != null) {
1729 match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
1730 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001731
1732 // Natch the Destination IPv4 Network prefix
1733 IPv4Net matchDstIPv4Net = flowEntryMatch.dstIPv4Net();
1734 if ((matchDstIPv4Net == null) && (flowPathMatch != null)) {
1735 matchDstIPv4Net = flowPathMatch.dstIPv4Net();
1736 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001737 if (matchDstIPv4Net != null) {
1738 match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
1739 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001740
1741 // Match the Source MAC address
1742 MACAddress matchSrcMac = flowEntryMatch.srcMac();
1743 if ((matchSrcMac == null) && (flowPathMatch != null)) {
1744 matchSrcMac = flowPathMatch.srcMac();
1745 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001746 if (matchSrcMac != null) {
1747 match.setDataLayerSource(matchSrcMac.toString());
1748 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1749 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001750
1751 // Match the Destination MAC address
1752 MACAddress matchDstMac = flowEntryMatch.dstMac();
1753 if ((matchDstMac == null) && (flowPathMatch != null)) {
1754 matchDstMac = flowPathMatch.dstMac();
1755 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001756 if (matchDstMac != null) {
1757 match.setDataLayerDestination(matchDstMac.toString());
1758 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1759 }
1760
1761 //
1762 // Fetch the actions
1763 //
1764 // TODO: For now we support only the "OUTPUT" actions.
1765 //
1766 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1767 List<OFAction> actions = new ArrayList<OFAction>();
1768 ArrayList<FlowEntryAction> flowEntryActions =
1769 flowEntry.flowEntryActions();
1770 for (FlowEntryAction flowEntryAction : flowEntryActions) {
1771 FlowEntryAction.ActionOutput actionOutput =
1772 flowEntryAction.actionOutput();
1773 if (actionOutput != null) {
1774 short actionOutputPort = actionOutput.port().value();
1775 OFActionOutput action = new OFActionOutput();
1776 // XXX: The max length is hard-coded for now
1777 action.setMaxLength((short)0xffff);
1778 action.setPort(actionOutputPort);
1779 actions.add(action);
1780 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1781 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1782 fm.setOutPort(actionOutputPort);
1783 }
1784 }
1785 }
1786
1787 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1788 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1789 .setPriority(PRIORITY_DEFAULT)
1790 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1791 .setCookie(cookie)
1792 .setCommand(flowModCommand)
1793 .setMatch(match)
1794 .setActions(actions)
1795 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1796
1797 //
1798 // TODO: Set the following flag
1799 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1800 // See method ForwardingBase::pushRoute()
1801 //
1802
1803 //
1804 // Write the message to the switch
1805 //
1806 try {
1807 messageDamper.write(mySwitch, fm, null);
1808 mySwitch.flush();
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001809 //
1810 // TODO: We should use the OpenFlow Barrier mechanism
1811 // to check for errors, and update the SwitchState
1812 // for a flow entry after the Barrier message is
1813 // is received.
1814 //
1815 // TODO: The FlowEntry Object in Titan should be set
1816 // to FE_SWITCH_UPDATED.
1817 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001818 } catch (IOException e) {
1819 log.error("Failure writing flow mod from network map", e);
1820 return false;
1821 }
1822 return true;
1823 }
1824
1825 /**
1826 * Remove a Flow Entry from a switch.
1827 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001828 * @param mySwitch the switch to remove the Flow Entry from.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001829 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001830 * @param flowEntry the flow entry to remove.
1831 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001832 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001833 public boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1834 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001835 //
1836 // The installFlowEntry() method implements both installation
1837 // and removal of flow entries.
1838 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001839 return (installFlowEntry(mySwitch, flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001840 }
1841
1842 /**
1843 * Install a Flow Entry on a remote controller.
1844 *
1845 * TODO: We need it now: Jono
1846 * - For now it will make a REST call to the remote controller.
1847 * - Internally, it needs to know the name of the remote controller.
1848 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001849 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001850 * @param flowEntry the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001851 * @return true on success, otherwise false.
1852 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001853 public boolean installRemoteFlowEntry(FlowPath flowPath,
1854 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001855 // TODO: We need it now: Jono
1856 // - For now it will make a REST call to the remote controller.
1857 // - Internally, it needs to know the name of the remote controller.
1858 return true;
1859 }
1860
1861 /**
1862 * Remove a flow entry on a remote controller.
1863 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001864 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001865 * @param flowEntry the flow entry to remove.
1866 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001867 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001868 public boolean removeRemoteFlowEntry(FlowPath flowPath,
1869 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001870 //
1871 // The installRemoteFlowEntry() method implements both installation
1872 // and removal of flow entries.
1873 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001874 return (installRemoteFlowEntry(flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001875 }
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001876
1877 /**
1878 * Store a path flow for measurement purpose.
1879 *
1880 * NOTE: The Flow Path argument does NOT contain flow entries.
1881 * The Shortest Path is computed, and the corresponding Flow Entries
1882 * are stored in the Flow Path.
1883 *
1884 * @param flowPath the Flow Path with the endpoints and the match
1885 * conditions to store.
1886 * @return the stored shortest-path flow on success, otherwise null.
1887 */
1888 @Override
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001889 public synchronized FlowPath measurementStorePathFlow(FlowPath flowPath) {
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001890 //
1891 // Prepare the Shortest Path computation if the first Flow Path
1892 //
1893 if (measurementStoredPaths.isEmpty())
1894 topoRouteService.prepareShortestPathTopo();
1895
1896 //
1897 // Compute the Shortest Path
1898 //
1899 DataPath dataPath =
1900 topoRouteService.getTopoShortestPath(flowPath.dataPath().srcPort(),
1901 flowPath.dataPath().dstPort());
1902 if (dataPath == null) {
1903 // We need the DataPath to populate the Network MAP
1904 dataPath = new DataPath();
1905 dataPath.setSrcPort(flowPath.dataPath().srcPort());
1906 dataPath.setDstPort(flowPath.dataPath().dstPort());
1907 }
1908
1909 //
1910 // Set the incoming port matching and the outgoing port output
1911 // actions for each flow entry.
1912 //
1913 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1914 // Set the incoming port matching
1915 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
1916 flowEntry.setFlowEntryMatch(flowEntryMatch);
1917 flowEntryMatch.enableInPort(flowEntry.inPort());
1918
1919 // Set the outgoing port output action
1920 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1921 if (flowEntryActions == null) {
1922 flowEntryActions = new ArrayList<FlowEntryAction>();
1923 flowEntry.setFlowEntryActions(flowEntryActions);
1924 }
1925 FlowEntryAction flowEntryAction = new FlowEntryAction();
1926 flowEntryAction.setActionOutput(flowEntry.outPort());
1927 flowEntryActions.add(flowEntryAction);
1928 }
1929
1930 //
1931 // Prepare the computed Flow Path
1932 //
1933 FlowPath computedFlowPath = new FlowPath();
1934 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1935 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1936 computedFlowPath.setDataPath(dataPath);
1937 computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
1938
1939 //
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001940 // Add the computed Flow Path to the internal storage
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001941 //
1942 measurementStoredPaths.add(computedFlowPath);
1943
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001944 log.debug("Measurement storing path {}",
1945 computedFlowPath.flowId().toString());
1946
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001947 return (computedFlowPath);
1948 }
1949
1950 /**
1951 * Install path flows for measurement purpose.
1952 *
1953 * @param numThreads the number of threads to use to install the path
1954 * flows.
1955 * @return true on success, otherwise false.
1956 */
1957 @Override
1958 public boolean measurementInstallPaths(Integer numThreads) {
1959 List<Thread> threads = new LinkedList<Thread>();
1960
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001961 // Create a copy of the Flow Paths to install
1962 final ConcurrentLinkedQueue<FlowPath> measurementProcessingPaths =
1963 new ConcurrentLinkedQueue<FlowPath>(measurementStoredPaths);
1964
1965 log.debug("Measurement Installing {} flows",
1966 measurementProcessingPaths.size());
Pavlin Radoslavove0938f32013-05-07 23:17:22 +00001967
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001968 //
1969 // Create the threads to install the Flow Paths
1970 //
1971 for (int i = 0; i < numThreads; i++) {
1972 Thread thread = new Thread(new Runnable() {
1973 @Override
1974 public void run() {
1975 while (true) {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001976 FlowPath flowPath = measurementProcessingPaths.poll();
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001977 if (flowPath == null)
1978 return;
1979 // Install the Flow Path
1980 FlowId flowId = new FlowId();
1981 String dataPathSummaryStr =
1982 flowPath.dataPath().dataPathSummary();
1983 addFlow(flowPath, flowId, dataPathSummaryStr);
1984 }
1985 }}, "Measurement Add Flow Path");
1986 threads.add(thread);
1987 }
1988
1989 //
1990 // Start processing
1991 //
1992 measurementEndTimeProcessingPaths = 0;
1993 measurementStartTimeProcessingPaths = System.nanoTime();
1994 for (Thread thread : threads) {
1995 thread.start();
1996 }
1997
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001998 // Want for all threads to complete
1999 for (Thread thread : threads) {
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002000 try {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00002001 thread.join();
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002002 } catch (InterruptedException e) {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00002003 log.debug("Exception waiting for a thread to install a Flow Path: ", e);
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002004 }
2005 }
2006
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00002007 // Record the end of processing
2008 measurementEndTimeProcessingPaths = System.nanoTime();
2009
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002010 return true;
2011 }
2012
2013 /**
2014 * Get the measurement time that took to install the path flows.
2015 *
2016 * @return the measurement time (in nanoseconds) it took to install
2017 * the path flows.
2018 */
2019 @Override
2020 public Long measurementGetInstallPathsTimeNsec() {
2021 return new Long(measurementEndTimeProcessingPaths -
2022 measurementStartTimeProcessingPaths);
2023 }
2024
2025 /**
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002026 * Clear the path flows stored for measurement purpose.
2027 *
2028 * @return true on success, otherwise false.
2029 */
2030 @Override
2031 public boolean measurementClearAllPaths() {
2032 measurementStoredPaths.clear();
2033 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavove0938f32013-05-07 23:17:22 +00002034 measurementStartTimeProcessingPaths = 0;
2035 measurementEndTimeProcessingPaths = 0;
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002036
2037 return true;
2038 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08002039}