blob: 6eed485274ba3bd487901bb142c52835d63d8769 [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;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080022import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080023import net.floodlightcontroller.core.module.FloodlightModuleContext;
24import net.floodlightcontroller.core.module.FloodlightModuleException;
25import net.floodlightcontroller.core.module.IFloodlightModule;
26import net.floodlightcontroller.core.module.IFloodlightService;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080027import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
28import net.floodlightcontroller.restserver.IRestApiService;
29import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080030import net.floodlightcontroller.util.DataPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080031import net.floodlightcontroller.util.DataPathEndpoints;
Jonathan Hart01f2d272013-04-04 20:03:46 -070032import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080033import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070034import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080035import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070036import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080037import net.floodlightcontroller.util.FlowEntrySwitchState;
38import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080039import net.floodlightcontroller.util.FlowId;
40import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070041import net.floodlightcontroller.util.IPv4Net;
42import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080043import net.floodlightcontroller.util.OFMessageDamper;
44import net.floodlightcontroller.util.Port;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070045import net.floodlightcontroller.util.SwitchPort;
HIGUCHI Yuta20514902013-06-12 11:24:16 -070046import net.onrc.onos.ofcontroller.core.INetMapStorage;
47import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
48import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
49import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
50import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
51import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoRouteService;
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 Radoslavov9556b142013-05-20 21:49:04 +0000101 Map<Long, ?> measurementShortestPathTopo = null;
Pavlin Radoslavovf5d80412013-05-24 05:14:07 +0000102 private String measurementPerFlowStr = new String();
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700103
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800104 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800105 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
106
107 // The periodic task(s)
Jonathan Hart50a94982013-04-10 14:49:51 -0700108 private ScheduledExecutorService mapReaderScheduler;
109 private ScheduledExecutorService shortestPathReconcileScheduler;
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700110
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700111 final Runnable mapReader = new Runnable() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800112 public void run() {
Pavlin Radoslavova75caea2013-04-10 19:11:26 -0700113 try {
114 runImpl();
115 } catch (Exception e) {
116 log.debug("Exception processing All Flow Entries from the Network MAP: ", e);
117 conn.endTx(Transaction.ROLLBACK);
118 return;
119 }
120 }
121
122 private void runImpl() {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700123 long startTime = System.nanoTime();
124 int counterAllFlowEntries = 0;
125 int counterMyNotUpdatedFlowEntries = 0;
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700126
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800127 if (floodlightProvider == null) {
128 log.debug("FloodlightProvider service not found!");
129 return;
130 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000131 Map<Long, IOFSwitch> mySwitches =
132 floodlightProvider.getSwitches();
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700133 LinkedList<IFlowEntry> addFlowEntries =
134 new LinkedList<IFlowEntry>();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000135 LinkedList<IFlowEntry> deleteFlowEntries =
136 new LinkedList<IFlowEntry>();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700137
138 //
Pankaj Berdea2e14a92013-04-15 11:59:15 -0700139 // Fetch all Flow Entries which need to be updated and select only my Flow Entries
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700140 // that need to be updated into the switches.
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700141 //
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700142 boolean processed_measurement_flow = false;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000143 Iterable<IFlowEntry> allFlowEntries =
Pankaj Berded1c38592013-04-10 22:46:40 -0700144 conn.utils().getAllSwitchNotUpdatedFlowEntries(conn);
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700145 for (IFlowEntry flowEntryObj : allFlowEntries) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700146 counterAllFlowEntries++;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000147
Pavlin Radoslavov6db8c6e2013-04-08 00:14:07 +0000148 String dpidStr = flowEntryObj.getSwitchDpid();
149 if (dpidStr == null)
150 continue;
151 Dpid dpid = new Dpid(dpidStr);
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 Radoslavov20d35a22013-04-05 10:16:15 -0700156 IFlowPath flowObj =
157 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
158 if (flowObj == null)
159 continue; // Should NOT happen
160 if (flowObj.getFlowId() == null)
161 continue; // Invalid entry
162
163 //
164 // NOTE: For now we process the DELETE before the ADD
165 // to cover the more common scenario.
166 // TODO: This is error prone and needs to be fixed!
167 //
Pavlin Radoslavov6db8c6e2013-04-08 00:14:07 +0000168 String userState = flowEntryObj.getUserState();
169 if (userState == null)
170 continue;
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700171 if (userState.equals("FE_USER_DELETE")) {
172 // An entry that needs to be deleted.
173 deleteFlowEntries.add(flowEntryObj);
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700174 installFlowEntry(mySwitch, flowObj, flowEntryObj);
175 } else {
176 addFlowEntries.add(flowEntryObj);
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700177 }
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700178 counterMyNotUpdatedFlowEntries++;
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 Radoslavov20d35a22013-04-05 10:16:15 -0700188 }
189
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700190 //
191 // Process the Flow Entries that need to be added
192 //
193 for (IFlowEntry flowEntryObj : addFlowEntries) {
194 IFlowPath flowObj =
195 conn.utils().getFlowPathByFlowEntry(conn,
196 flowEntryObj);
197 if (flowObj == null)
198 continue; // Should NOT happen
199 if (flowObj.getFlowId() == null)
200 continue; // Invalid entry
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700201
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700202 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700203 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000204 if (mySwitch == null)
205 continue; // Shouldn't happen
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700206 installFlowEntry(mySwitch, flowObj, flowEntryObj);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800207 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000208
209 //
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000210 // Delete all Flow Entries marked for deletion from the
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700211 // Network MAP.
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000212 //
213 // TODO: We should use the OpenFlow Barrier mechanism
214 // to check for errors, and delete the Flow Entries after the
215 // Barrier message is received.
216 //
217 while (! deleteFlowEntries.isEmpty()) {
218 IFlowEntry flowEntryObj = deleteFlowEntries.poll();
219 IFlowPath flowObj =
220 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
221 if (flowObj == null) {
222 log.debug("Did not find FlowPath to be deleted");
223 continue;
224 }
225 flowObj.removeFlowEntry(flowEntryObj);
226 conn.utils().removeFlowEntry(conn, flowEntryObj);
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000227 }
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700228
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700229 conn.endTx(Transaction.COMMIT);
230
231 if (processed_measurement_flow) {
232 long estimatedTime =
233 System.nanoTime() - modifiedMeasurementFlowTime;
234 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
235 (double)estimatedTime / 1000000000 + " sec";
236 log.debug(logMsg);
237 }
238
239 long estimatedTime = System.nanoTime() - startTime;
240 double rate = 0.0;
241 if (estimatedTime > 0)
242 rate = ((double)counterAllFlowEntries * 1000000000) / estimatedTime;
243 String logMsg = "MEASUREMENT: Processed AllFlowEntries: " +
244 counterAllFlowEntries + " MyNotUpdatedFlowEntries: " +
245 counterMyNotUpdatedFlowEntries + " in " +
246 (double)estimatedTime / 1000000000 + " sec: " +
247 rate + " paths/s";
248 log.debug(logMsg);
249 }
250 };
251
252 final Runnable shortestPathReconcile = new Runnable() {
253 public void run() {
Pavlin Radoslavova75caea2013-04-10 19:11:26 -0700254 try {
255 runImpl();
256 } catch (Exception e) {
257 log.debug("Exception processing All Flows from the Network MAP: ", e);
258 conn.endTx(Transaction.ROLLBACK);
259 return;
260 }
261 }
262
263 private void runImpl() {
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700264 long startTime = System.nanoTime();
265 int counterAllFlowPaths = 0;
266 int counterMyFlowPaths = 0;
267
268 if (floodlightProvider == null) {
269 log.debug("FloodlightProvider service not found!");
270 return;
271 }
272 Map<Long, IOFSwitch> mySwitches =
273 floodlightProvider.getSwitches();
274 LinkedList<IFlowPath> deleteFlows = new LinkedList<IFlowPath>();
275
276 boolean processed_measurement_flow = false;
277
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700278 //
279 // Fetch and recompute the Shortest Path for those
280 // Flow Paths this controller is responsible for.
281 //
Pavlin Radoslavov9556b142013-05-20 21:49:04 +0000282 Map<Long, ?> shortestPathTopo =
283 topoRouteService.prepareShortestPathTopo();
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700284 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700285 for (IFlowPath flowPathObj : allFlowPaths) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700286 counterAllFlowPaths++;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700287 if (flowPathObj == null)
288 continue;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700289
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700290 String srcDpidStr = flowPathObj.getSrcSwitch();
Pavlin Radoslavov6db8c6e2013-04-08 00:14:07 +0000291 if (srcDpidStr == null)
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700292 continue;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700293 Dpid srcDpid = new Dpid(srcDpidStr);
Pavlin Radoslavov2659a0b2013-04-03 20:30:40 -0700294 //
295 // Use the source DPID as a heuristic to decide
296 // which controller is responsible for maintaining the
297 // shortest path.
298 // NOTE: This heuristic is error-prone: if the switch
299 // goes away and no controller is responsible for that
300 // switch, then the original Flow Path is not cleaned-up
301 //
302 IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
303 if (mySwitch == null)
304 continue; // Ignore: not my responsibility
305
Pavlin Radoslavov99d1b152013-04-09 22:57:33 -0700306 // Test the Data Path Summary string
307 String dataPathSummaryStr = flowPathObj.getDataPathSummary();
308 if (dataPathSummaryStr == null)
309 continue; // Could be invalid entry?
310 if (dataPathSummaryStr.isEmpty())
311 continue; // No need to maintain this flow
312
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000313 //
314 // Test whether we need to complete the Flow cleanup,
315 // if the Flow has been deleted by the user.
316 //
317 String flowUserState = flowPathObj.getUserState();
318 if ((flowUserState != null)
319 && flowUserState.equals("FE_USER_DELETE")) {
320 Iterable<IFlowEntry> flowEntries = flowPathObj.getFlowEntries();
321 boolean empty = true; // TODO: an ugly hack
322 for (IFlowEntry flowEntryObj : flowEntries) {
323 empty = false;
324 break;
325 }
326 if (empty)
327 deleteFlows.add(flowPathObj);
328 }
329
Pavlin Radoslavov6db8c6e2013-04-08 00:14:07 +0000330 // Fetch the fields needed to recompute the shortest path
331 Short srcPortShort = flowPathObj.getSrcPort();
332 String dstDpidStr = flowPathObj.getDstSwitch();
333 Short dstPortShort = flowPathObj.getDstPort();
334 if ((srcPortShort == null) ||
335 (dstDpidStr == null) ||
336 (dstPortShort == null)) {
337 continue;
338 }
339
340 Port srcPort = new Port(srcPortShort);
341 Dpid dstDpid = new Dpid(dstDpidStr);
342 Port dstPort = new Port(dstPortShort);
343 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
344 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
345
Pavlin Radoslavov2659a0b2013-04-03 20:30:40 -0700346 counterMyFlowPaths++;
347
Pavlin Radoslavov832aa652013-03-29 16:21:59 -0700348 //
349 // NOTE: Using here the regular getShortestPath() method
350 // won't work here, because that method calls internally
351 // "conn.endTx(Transaction.COMMIT)", and that will
352 // invalidate all handlers to the Titan database.
353 // If we want to experiment with calling here
354 // getShortestPath(), we need to refactor that code
355 // to avoid closing the transaction.
356 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700357 DataPath dataPath =
Pavlin Radoslavov9556b142013-05-20 21:49:04 +0000358 topoRouteService.getTopoShortestPath(shortestPathTopo,
359 srcSwitchPort,
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700360 dstSwitchPort);
Pavlin Radoslavov4a325822013-04-02 22:16:59 +0000361 if (dataPath == null) {
362 // We need the DataPath to compare the paths
363 dataPath = new DataPath();
364 dataPath.setSrcPort(srcSwitchPort);
365 dataPath.setDstPort(dstSwitchPort);
366 }
367
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700368 String newDataPathSummaryStr = dataPath.dataPathSummary();
369 if (dataPathSummaryStr.equals(newDataPathSummaryStr))
370 continue; // Nothing changed
371
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700372 reconcileFlow(flowPathObj, dataPath);
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700373 }
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000374
375 //
376 // Delete all leftover Flows marked for deletion from the
377 // Network MAP.
378 //
379 while (! deleteFlows.isEmpty()) {
380 IFlowPath flowPathObj = deleteFlows.poll();
381 conn.utils().removeFlowPath(conn, flowPathObj);
382 }
383
Pavlin Radoslavov9556b142013-05-20 21:49:04 +0000384 topoRouteService.dropShortestPathTopo(shortestPathTopo);
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700385
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800386 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700387
388 if (processed_measurement_flow) {
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700389 long estimatedTime =
390 System.nanoTime() - modifiedMeasurementFlowTime;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700391 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
392 (double)estimatedTime / 1000000000 + " sec";
393 log.debug(logMsg);
394 }
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700395
396 long estimatedTime = System.nanoTime() - startTime;
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700397 double rate = 0.0;
398 if (estimatedTime > 0)
399 rate = ((double)counterAllFlowPaths * 1000000000) / estimatedTime;
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700400 String logMsg = "MEASUREMENT: Processed AllFlowPaths: " +
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700401 counterAllFlowPaths + " MyFlowPaths: " +
402 counterMyFlowPaths + " in " +
403 (double)estimatedTime / 1000000000 + " sec: " +
404 rate + " paths/s";
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700405 log.debug(logMsg);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800406 }
407 };
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700408
Jonathan Hart50a94982013-04-10 14:49:51 -0700409 //final ScheduledFuture<?> mapReaderHandle =
410 //mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800411
Jonathan Hart50a94982013-04-10 14:49:51 -0700412 //final ScheduledFuture<?> shortestPathReconcileHandle =
413 //shortestPathReconcileScheduler.scheduleAtFixedRate(shortestPathReconcile, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -0700414
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800415 @Override
416 public void init(String conf) {
Jonathan Hart50a94982013-04-10 14:49:51 -0700417 conn = GraphDBConnection.getInstance(conf);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800418 }
419
420 public void finalize() {
421 close();
422 }
423
424 @Override
425 public void close() {
426 conn.close();
427 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800428
429 @Override
430 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
431 Collection<Class<? extends IFloodlightService>> l =
432 new ArrayList<Class<? extends IFloodlightService>>();
433 l.add(IFlowService.class);
434 return l;
435 }
436
437 @Override
438 public Map<Class<? extends IFloodlightService>, IFloodlightService>
439 getServiceImpls() {
440 Map<Class<? extends IFloodlightService>,
441 IFloodlightService> m =
442 new HashMap<Class<? extends IFloodlightService>,
443 IFloodlightService>();
444 m.put(IFlowService.class, this);
445 return m;
446 }
447
448 @Override
449 public Collection<Class<? extends IFloodlightService>>
450 getModuleDependencies() {
451 Collection<Class<? extends IFloodlightService>> l =
452 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800453 l.add(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700454 l.add(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800455 l.add(IRestApiService.class);
456 return l;
457 }
458
459 @Override
460 public void init(FloodlightModuleContext context)
461 throws FloodlightModuleException {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700462 this.context = context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800463 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700464 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800465 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800466 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
467 EnumSet.of(OFType.FLOW_MOD),
468 OFMESSAGE_DAMPER_TIMEOUT);
469 // TODO: An ugly hack!
470 String conf = "/tmp/cassandra.titan";
471 this.init(conf);
Jonathan Hart50a94982013-04-10 14:49:51 -0700472
473 mapReaderScheduler = Executors.newScheduledThreadPool(1);
474 shortestPathReconcileScheduler = Executors.newScheduledThreadPool(1);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800475 }
476
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -0700477 private synchronized long getNextFlowEntryId() {
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000478 //
479 // Generate the next Flow Entry ID.
480 // NOTE: For now, the higher 32 bits are random, and
481 // the lower 32 bits are sequential.
482 // In the future, we need a better allocation mechanism.
483 //
484 if ((nextFlowEntryIdSuffix & 0xffffffffL) == 0xffffffffL) {
485 nextFlowEntryIdPrefix = randomGenerator.nextInt();
486 nextFlowEntryIdSuffix = 0;
487 } else {
488 nextFlowEntryIdSuffix++;
489 }
490 long result = (long)nextFlowEntryIdPrefix << 32;
491 result = result | (0xffffffffL & nextFlowEntryIdSuffix);
492 return result;
493 }
494
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800495 @Override
496 public void startUp(FloodlightModuleContext context) {
Jonathan Hart50a94982013-04-10 14:49:51 -0700497 restApi.addRestletRoutable(new FlowWebRoutable());
498
499 // Initialize the Flow Entry ID generator
500 nextFlowEntryIdPrefix = randomGenerator.nextInt();
501
502 mapReaderScheduler.scheduleAtFixedRate(
Pankaj Berde55f121a2013-04-23 15:42:54 -0700503 mapReader, 1, 1, TimeUnit.SECONDS);
Jonathan Hart50a94982013-04-10 14:49:51 -0700504 shortestPathReconcileScheduler.scheduleAtFixedRate(
Pankaj Berdea2e14a92013-04-15 11:59:15 -0700505 shortestPathReconcile, 100, 100, TimeUnit.MILLISECONDS);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800506 }
507
508 /**
509 * Add a flow.
510 *
511 * Internally, ONOS will automatically register the installer for
512 * receiving Flow Path Notifications for that path.
513 *
514 * @param flowPath the Flow Path to install.
515 * @param flowId the return-by-reference Flow ID as assigned internally.
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700516 * @param dataPathSummaryStr the data path summary string if the added
517 * flow will be maintained internally, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800518 * @return true on success, otherwise false.
519 */
520 @Override
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700521 public boolean addFlow(FlowPath flowPath, FlowId flowId,
522 String dataPathSummaryStr) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700523 /*
524 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700525 if (flowPath.flowId().value() == measurementFlowId) {
526 modifiedMeasurementFlowTime = System.nanoTime();
527 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700528 */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800529
530 IFlowPath flowObj = null;
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000531 boolean found = false;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800532 try {
533 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
534 != null) {
535 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
536 flowPath.flowId().toString());
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000537 found = true;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800538 } else {
539 flowObj = conn.utils().newFlowPath(conn);
540 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
541 flowPath.flowId().toString());
542 }
543 } catch (Exception e) {
544 // TODO: handle exceptions
545 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000546
547 StringWriter sw = new StringWriter();
548 e.printStackTrace(new PrintWriter(sw));
549 String stacktrace = sw.toString();
550
551 log.error(":addFlow FlowId:{} failed: {}",
552 flowPath.flowId().toString(),
553 stacktrace);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800554 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700555 if (flowObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000556 log.error(":addFlow FlowId:{} failed: Flow object not created",
557 flowPath.flowId().toString());
558 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800559 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700560 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800561
562 //
563 // Set the Flow key:
564 // - flowId
565 //
566 flowObj.setFlowId(flowPath.flowId().toString());
567 flowObj.setType("flow");
568
569 //
570 // Set the Flow attributes:
571 // - flowPath.installerId()
572 // - flowPath.dataPath().srcPort()
573 // - flowPath.dataPath().dstPort()
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700574 // - flowPath.matchEthernetFrameType()
575 // - flowPath.matchSrcIPv4Net()
576 // - flowPath.matchDstIPv4Net()
577 // - flowPath.matchSrcMac()
578 // - flowPath.matchDstMac()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800579 //
580 flowObj.setInstallerId(flowPath.installerId().toString());
581 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
582 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
583 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
584 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700585 if (flowPath.flowEntryMatch().matchEthernetFrameType()) {
586 flowObj.setMatchEthernetFrameType(flowPath.flowEntryMatch().ethernetFrameType());
587 }
588 if (flowPath.flowEntryMatch().matchSrcIPv4Net()) {
589 flowObj.setMatchSrcIPv4Net(flowPath.flowEntryMatch().srcIPv4Net().toString());
590 }
591 if (flowPath.flowEntryMatch().matchDstIPv4Net()) {
592 flowObj.setMatchDstIPv4Net(flowPath.flowEntryMatch().dstIPv4Net().toString());
593 }
594 if (flowPath.flowEntryMatch().matchSrcMac()) {
595 flowObj.setMatchSrcMac(flowPath.flowEntryMatch().srcMac().toString());
596 }
597 if (flowPath.flowEntryMatch().matchDstMac()) {
598 flowObj.setMatchDstMac(flowPath.flowEntryMatch().dstMac().toString());
599 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800600
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700601 if (dataPathSummaryStr != null) {
602 flowObj.setDataPathSummary(dataPathSummaryStr);
603 } else {
604 flowObj.setDataPathSummary("");
605 }
606
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000607 if (found)
608 flowObj.setUserState("FE_USER_MODIFY");
609 else
610 flowObj.setUserState("FE_USER_ADD");
611
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800612 // Flow edges:
613 // HeadFE
614
615
616 //
617 // Flow Entries:
618 // flowPath.dataPath().flowEntries()
619 //
620 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700621 if (addFlowEntry(flowObj, flowEntry) == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000622 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800623 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700624 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800625 }
626 conn.endTx(Transaction.COMMIT);
627
628 //
629 // TODO: We need a proper Flow ID allocation mechanism.
630 //
631 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700632
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800633 return true;
634 }
635
636 /**
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700637 * Add a flow entry to the Network MAP.
638 *
639 * @param flowObj the corresponding Flow Path object for the Flow Entry.
640 * @param flowEntry the Flow Entry to install.
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700641 * @return the added Flow Entry object on success, otherwise null.
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700642 */
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700643 private IFlowEntry addFlowEntry(IFlowPath flowObj, FlowEntry flowEntry) {
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700644 // Flow edges
645 // HeadFE (TODO)
646
647 //
648 // Assign the FlowEntry ID.
649 //
650 if ((flowEntry.flowEntryId() == null) ||
651 (flowEntry.flowEntryId().value() == 0)) {
652 long id = getNextFlowEntryId();
653 flowEntry.setFlowEntryId(new FlowEntryId(id));
654 }
655
656 IFlowEntry flowEntryObj = null;
657 boolean found = false;
658 try {
659 if ((flowEntryObj =
660 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
661 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
662 flowEntry.flowEntryId().toString());
663 found = true;
664 } else {
665 flowEntryObj = conn.utils().newFlowEntry(conn);
666 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
667 flowEntry.flowEntryId().toString());
668 }
669 } catch (Exception e) {
670 log.error(":addFlow FlowEntryId:{} failed",
671 flowEntry.flowEntryId().toString());
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700672 return null;
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700673 }
674 if (flowEntryObj == null) {
675 log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created",
676 flowEntry.flowEntryId().toString());
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700677 return null;
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700678 }
679
680 //
681 // Set the Flow Entry key:
682 // - flowEntry.flowEntryId()
683 //
684 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
685 flowEntryObj.setType("flow_entry");
686
687 //
688 // Set the Flow Entry Edges and attributes:
689 // - Switch edge
690 // - InPort edge
691 // - OutPort edge
692 //
693 // - flowEntry.flowEntryMatch()
694 // - flowEntry.flowEntryActions()
695 // - flowEntry.dpid()
696 // - flowEntry.flowEntryUserState()
697 // - flowEntry.flowEntrySwitchState()
698 // - flowEntry.flowEntryErrorState()
699 // - flowEntry.matchInPort()
700 // - flowEntry.matchEthernetFrameType()
701 // - flowEntry.matchSrcIPv4Net()
702 // - flowEntry.matchDstIPv4Net()
703 // - flowEntry.matchSrcMac()
704 // - flowEntry.matchDstMac()
705 // - flowEntry.actionOutput()
706 //
707 ISwitchObject sw =
708 conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
709 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
710 flowEntryObj.setSwitch(sw);
711 if (flowEntry.flowEntryMatch().matchInPort()) {
712 IPortObject inport =
713 conn.utils().searchPort(conn, flowEntry.dpid().toString(),
714 flowEntry.flowEntryMatch().inPort().value());
715 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
716 flowEntryObj.setInPort(inport);
717 }
718 if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
719 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
720 }
721 if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
722 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
723 }
724 if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
725 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
726 }
727 if (flowEntry.flowEntryMatch().matchSrcMac()) {
728 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
729 }
730 if (flowEntry.flowEntryMatch().matchDstMac()) {
731 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
732 }
733
734 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
735 if (fa.actionOutput() != null) {
736 IPortObject outport =
737 conn.utils().searchPort(conn,
738 flowEntry.dpid().toString(),
739 fa.actionOutput().port().value());
740 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
741 flowEntryObj.setOutPort(outport);
742 }
743 }
744 // TODO: Hacks with hard-coded state names!
745 if (found)
746 flowEntryObj.setUserState("FE_USER_MODIFY");
747 else
748 flowEntryObj.setUserState("FE_USER_ADD");
749 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
750 //
751 // TODO: Take care of the FlowEntryErrorState.
752 //
753
754 // Flow Entries edges:
755 // Flow
756 // NextFE (TODO)
757 if (! found) {
758 flowObj.addFlowEntry(flowEntryObj);
759 flowEntryObj.setFlow(flowObj);
760 }
761
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -0700762 return flowEntryObj;
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700763 }
764
765 /**
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000766 * Delete all previously added flows.
767 *
768 * @return true on success, otherwise false.
769 */
770 @Override
771 public boolean deleteAllFlows() {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000772 List<Thread> threads = new LinkedList<Thread>();
773 final ConcurrentLinkedQueue<FlowId> concurrentAllFlowIds =
774 new ConcurrentLinkedQueue<FlowId>();
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000775
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000776 // Get all Flow IDs
777 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
778 for (IFlowPath flowPathObj : allFlowPaths) {
779 if (flowPathObj == null)
780 continue;
781 String flowIdStr = flowPathObj.getFlowId();
782 if (flowIdStr == null)
783 continue;
784 FlowId flowId = new FlowId(flowIdStr);
785 concurrentAllFlowIds.add(flowId);
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000786 }
787
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000788 // Delete all flows one-by-one
789 for (FlowId flowId : concurrentAllFlowIds)
790 deleteFlow(flowId);
791
792 /*
793 * TODO: A faster mechanism to delete the Flow Paths by using
794 * a number of threads. Commented-out for now.
795 */
796 /*
797 //
798 // Create the threads to delete the Flow Paths
799 //
800 for (int i = 0; i < 10; i++) {
801 Thread thread = new Thread(new Runnable() {
802 @Override
803 public void run() {
804 while (true) {
805 FlowId flowId = concurrentAllFlowIds.poll();
806 if (flowId == null)
807 return;
808 deleteFlow(flowId);
809 }
810 }}, "Delete All Flow Paths");
811 threads.add(thread);
812 }
813
814 // Start processing
815 for (Thread thread : threads) {
816 thread.start();
817 }
818
Pavlin Radoslavovf5d80412013-05-24 05:14:07 +0000819 // Wait for all threads to complete
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000820 for (Thread thread : threads) {
821 try {
822 thread.join();
823 } catch (InterruptedException e) {
824 log.debug("Exception waiting for a thread to delete a Flow Path: ", e);
825 }
826 }
827 */
828
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000829 return true;
830 }
831
832 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800833 * Delete a previously added flow.
834 *
835 * @param flowId the Flow ID of the flow to delete.
836 * @return true on success, otherwise false.
837 */
838 @Override
839 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700840 /*
841 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700842 if (flowId.value() == measurementFlowId) {
843 modifiedMeasurementFlowTime = System.nanoTime();
844 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700845 */
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700846
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800847 IFlowPath flowObj = null;
848 //
849 // We just mark the entries for deletion,
850 // and let the switches remove each individual entry after
851 // it has been removed from the switches.
852 //
853 try {
854 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
855 != null) {
856 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
857 flowId.toString());
858 } else {
859 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
860 flowId.toString());
861 }
862 } catch (Exception e) {
863 // TODO: handle exceptions
864 conn.endTx(Transaction.ROLLBACK);
865 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
866 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700867 if (flowObj == null) {
868 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800869 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700870 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800871
872 //
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000873 // Find and mark for deletion all Flow Entries,
874 // and the Flow itself.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800875 //
Pavlin Radoslavov710e2a72013-04-08 02:31:05 +0000876 flowObj.setUserState("FE_USER_DELETE");
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800877 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
878 boolean empty = true; // TODO: an ugly hack
879 for (IFlowEntry flowEntryObj : flowEntries) {
880 empty = false;
881 // flowObj.removeFlowEntry(flowEntryObj);
882 // conn.utils().removeFlowEntry(conn, flowEntryObj);
883 flowEntryObj.setUserState("FE_USER_DELETE");
884 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
885 }
886 // Remove from the database empty flows
887 if (empty)
888 conn.utils().removeFlowPath(conn, flowObj);
889 conn.endTx(Transaction.COMMIT);
890
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800891 return true;
892 }
893
894 /**
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000895 * Clear the state for all previously added flows.
896 *
897 * @return true on success, otherwise false.
898 */
899 @Override
900 public boolean clearAllFlows() {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000901 List<FlowId> allFlowIds = new LinkedList<FlowId>();
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000902
Pavlin Radoslavov759772f2013-05-20 20:50:00 +0000903 // Get all Flow IDs
904 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
905 for (IFlowPath flowPathObj : allFlowPaths) {
906 if (flowPathObj == null)
907 continue;
908 String flowIdStr = flowPathObj.getFlowId();
909 if (flowIdStr == null)
910 continue;
911 FlowId flowId = new FlowId(flowIdStr);
912 allFlowIds.add(flowId);
913 }
914
915 // Clear all flows one-by-one
916 for (FlowId flowId : allFlowIds) {
917 clearFlow(flowId);
Pavlin Radoslavovbaea9242013-05-08 00:20:09 +0000918 }
919
920 return true;
921 }
922
923 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700924 * Clear the state for a previously added flow.
925 *
926 * @param flowId the Flow ID of the flow to clear.
927 * @return true on success, otherwise false.
928 */
929 @Override
930 public boolean clearFlow(FlowId flowId) {
931 IFlowPath flowObj = null;
932 try {
933 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
934 != null) {
935 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
936 flowId.toString());
937 } else {
938 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
939 flowId.toString());
940 }
941 } catch (Exception e) {
942 // TODO: handle exceptions
943 conn.endTx(Transaction.ROLLBACK);
944 log.error(":clearFlow FlowId:{} failed", flowId.toString());
945 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700946 if (flowObj == null) {
947 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700948 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700949 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700950
951 //
952 // Remove all Flow Entries
953 //
954 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
955 for (IFlowEntry flowEntryObj : flowEntries) {
956 flowObj.removeFlowEntry(flowEntryObj);
957 conn.utils().removeFlowEntry(conn, flowEntryObj);
958 }
959 // Remove the Flow itself
960 conn.utils().removeFlowPath(conn, flowObj);
961 conn.endTx(Transaction.COMMIT);
962
963 return true;
964 }
965
966 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800967 * Get a previously added flow.
968 *
969 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800970 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800971 */
972 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800973 public FlowPath getFlow(FlowId flowId) {
974 IFlowPath flowObj = null;
975 try {
976 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
977 != null) {
978 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
979 flowId.toString());
980 } else {
981 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
982 flowId.toString());
983 }
984 } catch (Exception e) {
985 // TODO: handle exceptions
986 conn.endTx(Transaction.ROLLBACK);
987 log.error(":getFlow FlowId:{} failed", flowId.toString());
988 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700989 if (flowObj == null) {
990 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800991 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700992 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800993
994 //
995 // Extract the Flow state
996 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800997 FlowPath flowPath = extractFlowPath(flowObj);
998 conn.endTx(Transaction.COMMIT);
999
1000 return flowPath;
1001 }
1002
1003 /**
1004 * Get all previously added flows by a specific installer for a given
1005 * data path endpoints.
1006 *
1007 * @param installerId the Caller ID of the installer of the flow to get.
1008 * @param dataPathEndpoints the data path endpoints of the flow to get.
1009 * @return the Flow Paths if found, otherwise null.
1010 */
1011 @Override
1012 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
1013 DataPathEndpoints dataPathEndpoints) {
1014 //
1015 // TODO: The implementation below is not optimal:
1016 // We fetch all flows, and then return only the subset that match
1017 // the query conditions.
1018 // We should use the appropriate Titan/Gremlin query to filter-out
1019 // the flows as appropriate.
1020 //
1021 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001022 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001023
1024 if (allFlows == null) {
1025 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001026 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001027 }
1028
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001029 for (FlowPath flow : allFlows) {
1030 //
1031 // TODO: String-based comparison is sub-optimal.
1032 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -08001033 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001034 //
1035 if (! flow.installerId().toString().equals(installerId.toString()))
1036 continue;
1037 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
1038 continue;
1039 }
1040 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
1041 continue;
1042 }
1043 flowPaths.add(flow);
1044 }
1045
1046 if (flowPaths.isEmpty()) {
1047 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001048 } else {
1049 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
1050 }
1051
1052 return flowPaths;
1053 }
1054
1055 /**
1056 * Get all installed flows by all installers for given data path endpoints.
1057 *
1058 * @param dataPathEndpoints the data path endpoints of the flows to get.
1059 * @return the Flow Paths if found, otherwise null.
1060 */
1061 @Override
1062 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
1063 //
1064 // TODO: The implementation below is not optimal:
1065 // We fetch all flows, and then return only the subset that match
1066 // the query conditions.
1067 // We should use the appropriate Titan/Gremlin query to filter-out
1068 // the flows as appropriate.
1069 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001070 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1071 ArrayList<FlowPath> allFlows = getAllFlows();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001072
1073 if (allFlows == null) {
1074 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001075 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001076 }
1077
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001078 for (FlowPath flow : allFlows) {
1079 //
1080 // TODO: String-based comparison is sub-optimal.
1081 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -08001082 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001083 //
1084 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
1085 continue;
1086 }
1087 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
1088 continue;
1089 }
1090 flowPaths.add(flow);
1091 }
1092
1093 if (flowPaths.isEmpty()) {
1094 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001095 } else {
1096 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
1097 }
1098
1099 return flowPaths;
1100 }
1101
1102 /**
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001103 * Get summary of all installed flows by all installers in a given range
1104 *
1105 * @param flowId the data path endpoints of the flows to get.
1106 * @param maxFlows: the maximum number of flows to be returned
1107 * @return the Flow Paths if found, otherwise null.
1108 */
1109 @Override
Jonathan Hart01f2d272013-04-04 20:03:46 -07001110 public ArrayList<IFlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
Jonathan Hartf5315fb2013-04-05 11:41:56 -07001111
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001112 // TODO: The implementation below is not optimal:
1113 // We fetch all flows, and then return only the subset that match
1114 // the query conditions.
1115 // We should use the appropriate Titan/Gremlin query to filter-out
1116 // the flows as appropriate.
1117 //
Jonathan Hart01f2d272013-04-04 20:03:46 -07001118 //ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001119
Jonathan Hart01f2d272013-04-04 20:03:46 -07001120 ArrayList<IFlowPath> flowPathsWithoutFlowEntries = getAllFlowsWithoutFlowEntries();
1121
Jonathan Hartf5315fb2013-04-05 11:41:56 -07001122 Collections.sort(flowPathsWithoutFlowEntries,
1123 new Comparator<IFlowPath>(){
1124 @Override
1125 public int compare(IFlowPath first, IFlowPath second) {
1126 // TODO Auto-generated method stub
1127 long result = new FlowId(first.getFlowId()).value()
1128 - new FlowId(second.getFlowId()).value();
1129 if (result > 0) return 1;
1130 else if (result < 0) return -1;
1131 else return 0;
1132 }
1133 }
1134 );
1135
Jonathan Hart01f2d272013-04-04 20:03:46 -07001136 return flowPathsWithoutFlowEntries;
1137
1138 /*
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001139 ArrayList<FlowPath> allFlows = getAllFlows();
Jonathan Hart01f2d272013-04-04 20:03:46 -07001140
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001141 if (allFlows == null) {
1142 log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001143 return flowPaths;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001144 }
1145
Umesh Krishnaswamy244b4ae2013-03-29 12:05:15 -07001146 Collections.sort(allFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001147
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001148 for (FlowPath flow : allFlows) {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001149 flow.setFlowEntryMatch(null);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001150
Pavlin Radoslavov96b43422013-04-04 19:14:56 -07001151 // start from desired flowId
1152 if (flow.flowId().value() < flowId.value()) {
1153 continue;
1154 }
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001155
1156 // Summarize by making null flow entry fields that are not relevant to report
1157 for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
1158 flowEntry.setFlowEntryActions(null);
1159 flowEntry.setFlowEntryMatch(null);
1160 }
1161
1162 flowPaths.add(flow);
1163 if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
1164 break;
1165 }
1166 }
1167
1168 if (flowPaths.isEmpty()) {
1169 log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001170 } else {
1171 log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
1172 }
1173
1174 return flowPaths;
Jonathan Hart01f2d272013-04-04 20:03:46 -07001175 */
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001176 }
1177
1178 /**
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001179 * Get all installed flows by all installers.
1180 *
1181 * @return the Flow Paths if found, otherwise null.
1182 */
1183 @Override
1184 public ArrayList<FlowPath> getAllFlows() {
1185 Iterable<IFlowPath> flowPathsObj = null;
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001186 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001187
1188 try {
1189 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
1190 log.debug("Get all FlowPaths: found FlowPaths");
1191 } else {
1192 log.debug("Get all FlowPaths: no FlowPaths found");
1193 }
1194 } catch (Exception e) {
1195 // TODO: handle exceptions
1196 conn.endTx(Transaction.ROLLBACK);
1197 log.error(":getAllFlowPaths failed");
1198 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001199 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
1200 conn.endTx(Transaction.COMMIT);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001201 return flowPaths; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001202 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001203
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001204 for (IFlowPath flowObj : flowPathsObj) {
1205 //
1206 // Extract the Flow state
1207 //
1208 FlowPath flowPath = extractFlowPath(flowObj);
Pavlin Radoslavov3f2af732013-03-29 15:29:35 -07001209 if (flowPath != null)
1210 flowPaths.add(flowPath);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001211 }
1212
1213 conn.endTx(Transaction.COMMIT);
1214
1215 return flowPaths;
1216 }
Jonathan Hart01f2d272013-04-04 20:03:46 -07001217
1218 public ArrayList<IFlowPath> getAllFlowsWithoutFlowEntries(){
1219 Iterable<IFlowPath> flowPathsObj = null;
1220 ArrayList<IFlowPath> flowPathsObjArray = new ArrayList<IFlowPath>();
1221 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1222
Jonathan Harte6e91872013-04-13 11:10:32 -07001223 conn.endTx(Transaction.COMMIT);
1224
Jonathan Hart01f2d272013-04-04 20:03:46 -07001225 try {
1226 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
1227 log.debug("Get all FlowPaths: found FlowPaths");
1228 } else {
1229 log.debug("Get all FlowPaths: no FlowPaths found");
1230 }
1231 } catch (Exception e) {
1232 // TODO: handle exceptions
1233 conn.endTx(Transaction.ROLLBACK);
1234 log.error(":getAllFlowPaths failed");
1235 }
1236 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
1237 return new ArrayList<IFlowPath>(); // No Flows found
1238 }
1239
1240 for (IFlowPath flowObj : flowPathsObj){
1241 flowPathsObjArray.add(flowObj);
1242 }
1243 /*
1244 for (IFlowPath flowObj : flowPathsObj) {
1245 //
1246 // Extract the Flow state
1247 //
1248 FlowPath flowPath = extractFlowPath(flowObj);
1249 if (flowPath != null)
1250 flowPaths.add(flowPath);
1251 }
1252 */
1253
1254 //conn.endTx(Transaction.COMMIT);
1255
1256 return flowPathsObjArray;
1257 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001258
1259 /**
1260 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
1261 *
1262 * @param flowObj the object to extract the Flow Path State from.
1263 * @return the extracted Flow Path State.
1264 */
1265 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001266 //
1267 // Extract the Flow state
1268 //
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001269 String flowIdStr = flowObj.getFlowId();
1270 String installerIdStr = flowObj.getInstallerId();
1271 String srcSwitchStr = flowObj.getSrcSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001272 Short srcPortShort = flowObj.getSrcPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001273 String dstSwitchStr = flowObj.getDstSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001274 Short dstPortShort = flowObj.getDstPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001275
1276 if ((flowIdStr == null) ||
1277 (installerIdStr == null) ||
1278 (srcSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001279 (srcPortShort == null) ||
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001280 (dstSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001281 (dstPortShort == null)) {
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001282 // TODO: A work-around, becauuse of some bogus database objects
1283 return null;
1284 }
1285
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001286 FlowPath flowPath = new FlowPath();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001287 flowPath.setFlowId(new FlowId(flowIdStr));
1288 flowPath.setInstallerId(new CallerId(installerIdStr));
1289 flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001290 flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001291 flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001292 flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001293 //
1294 // Extract the match conditions common for all Flow Entries
1295 //
1296 {
1297 FlowEntryMatch match = new FlowEntryMatch();
1298 Short matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1299 if (matchEthernetFrameType != null)
1300 match.enableEthernetFrameType(matchEthernetFrameType);
1301 String matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1302 if (matchSrcIPv4Net != null)
1303 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1304 String matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1305 if (matchDstIPv4Net != null)
1306 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1307 String matchSrcMac = flowObj.getMatchSrcMac();
1308 if (matchSrcMac != null)
1309 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1310 String matchDstMac = flowObj.getMatchDstMac();
1311 if (matchDstMac != null)
1312 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1313 flowPath.setFlowEntryMatch(match);
1314 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001315
1316 //
1317 // Extract all Flow Entries
1318 //
1319 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1320 for (IFlowEntry flowEntryObj : flowEntries) {
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001321 FlowEntry flowEntry = extractFlowEntry(flowEntryObj);
1322 if (flowEntry == null)
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001323 continue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001324 flowPath.dataPath().flowEntries().add(flowEntry);
1325 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001326
1327 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001328 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001329
1330 /**
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001331 * Extract Flow Entry State from a Titan Database Object @ref IFlowEntry.
1332 *
1333 * @param flowEntryObj the object to extract the Flow Entry State from.
1334 * @return the extracted Flow Entry State.
1335 */
1336 private FlowEntry extractFlowEntry(IFlowEntry flowEntryObj) {
1337 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1338 String switchDpidStr = flowEntryObj.getSwitchDpid();
1339 String userState = flowEntryObj.getUserState();
1340 String switchState = flowEntryObj.getSwitchState();
1341
1342 if ((flowEntryIdStr == null) ||
1343 (switchDpidStr == null) ||
1344 (userState == null) ||
1345 (switchState == null)) {
1346 // TODO: A work-around, becauuse of some bogus database objects
1347 return null;
1348 }
1349
1350 FlowEntry flowEntry = new FlowEntry();
1351 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
1352 flowEntry.setDpid(new Dpid(switchDpidStr));
1353
1354 //
1355 // Extract the match conditions
1356 //
1357 FlowEntryMatch match = new FlowEntryMatch();
1358 Short matchInPort = flowEntryObj.getMatchInPort();
1359 if (matchInPort != null)
1360 match.enableInPort(new Port(matchInPort));
1361 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1362 if (matchEthernetFrameType != null)
1363 match.enableEthernetFrameType(matchEthernetFrameType);
1364 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1365 if (matchSrcIPv4Net != null)
1366 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1367 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1368 if (matchDstIPv4Net != null)
1369 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1370 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1371 if (matchSrcMac != null)
1372 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1373 String matchDstMac = flowEntryObj.getMatchDstMac();
1374 if (matchDstMac != null)
1375 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1376 flowEntry.setFlowEntryMatch(match);
1377
1378 //
1379 // Extract the actions
1380 //
1381 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
1382 Short actionOutputPort = flowEntryObj.getActionOutput();
1383 if (actionOutputPort != null) {
1384 FlowEntryAction action = new FlowEntryAction();
1385 action.setActionOutput(new Port(actionOutputPort));
1386 actions.add(action);
1387 }
1388 flowEntry.setFlowEntryActions(actions);
1389 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
1390 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
1391 //
1392 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
1393 // and FlowEntryErrorState.
1394 //
1395 return flowEntry;
1396 }
1397
1398 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001399 * Add and maintain a shortest-path flow.
1400 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001401 * NOTE: The Flow Path argument does NOT contain flow entries.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001402 *
1403 * @param flowPath the Flow Path with the endpoints and the match
1404 * conditions to install.
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001405 * @return the added shortest-path flow on success, otherwise null.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001406 */
1407 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001408 public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001409 //
Pavlin Radoslavov8b4b0592013-04-10 04:33:33 +00001410 // Don't do the shortest path computation here.
1411 // Instead, let the Flow reconciliation thread take care of it.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001412 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001413
Pavlin Radoslavov8b4b0592013-04-10 04:33:33 +00001414 // We need the DataPath to populate the Network MAP
1415 DataPath dataPath = new DataPath();
1416 dataPath.setSrcPort(flowPath.dataPath().srcPort());
1417 dataPath.setDstPort(flowPath.dataPath().dstPort());
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001418
1419 //
1420 // Prepare the computed Flow Path
1421 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001422 FlowPath computedFlowPath = new FlowPath();
1423 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1424 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1425 computedFlowPath.setDataPath(dataPath);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001426 computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001427
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001428 FlowId flowId = new FlowId();
Pavlin Radoslavov8b4b0592013-04-10 04:33:33 +00001429 String dataPathSummaryStr = dataPath.dataPathSummary();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001430 if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001431 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001432
1433 // TODO: Mark the flow for maintenance purpose
1434
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001435 return (computedFlowPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001436 }
1437
1438 /**
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001439 * Reconcile a flow.
1440 *
1441 * @param flowObj the flow that needs to be reconciliated.
1442 * @param newDataPath the new data path to use.
1443 * @return true on success, otherwise false.
1444 */
1445 public boolean reconcileFlow(IFlowPath flowObj, DataPath newDataPath) {
1446 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
1447
1448 //
1449 // Set the incoming port matching and the outgoing port output
1450 // actions for each flow entry.
1451 //
1452 for (FlowEntry flowEntry : newDataPath.flowEntries()) {
1453 // Set the incoming port matching
1454 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
1455 flowEntry.setFlowEntryMatch(flowEntryMatch);
1456 flowEntryMatch.enableInPort(flowEntry.inPort());
1457
1458 // Set the outgoing port output action
1459 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1460 if (flowEntryActions == null) {
1461 flowEntryActions = new ArrayList<FlowEntryAction>();
1462 flowEntry.setFlowEntryActions(flowEntryActions);
1463 }
1464 FlowEntryAction flowEntryAction = new FlowEntryAction();
1465 flowEntryAction.setActionOutput(flowEntry.outPort());
1466 flowEntryActions.add(flowEntryAction);
1467 }
1468
1469 //
1470 // Remove the old Flow Entries, and add the new Flow Entries
1471 //
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001472 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1473 LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
1474 for (IFlowEntry flowEntryObj : flowEntries) {
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001475 flowEntryObj.setUserState("FE_USER_DELETE");
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -07001476 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001477 }
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001478 for (FlowEntry flowEntry : newDataPath.flowEntries()) {
Pavlin Radoslavov6fb76d12013-04-09 22:52:25 -07001479 addFlowEntry(flowObj, flowEntry);
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001480 }
1481
1482 //
1483 // Set the Data Path Summary
1484 //
1485 String dataPathSummaryStr = newDataPath.dataPathSummary();
1486 flowObj.setDataPathSummary(dataPathSummaryStr);
1487
1488 return true;
1489 }
1490
1491 /**
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001492 * Reconcile all flows in a set.
1493 *
1494 * @param flowObjSet the set of flows that need to be reconciliated.
1495 */
1496 public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
1497 if (! flowObjSet.iterator().hasNext())
1498 return;
Pavlin Radoslavov0eeb15d2013-04-05 10:23:51 -07001499 // TODO: Not implemented/used yet.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001500 }
1501
1502 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001503 * Install a Flow Entry on a switch.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001504 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001505 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001506 * @param flowObj the flow path object for the flow entry to install.
1507 * @param flowEntryObj the flow entry object to install.
1508 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001509 */
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001510 public boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
1511 IFlowEntry flowEntryObj) {
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001512 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1513 if (flowEntryIdStr == null)
1514 return false;
1515 FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001516 String userState = flowEntryObj.getUserState();
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001517 if (userState == null)
1518 return false;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001519
1520 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001521 // Create the Open Flow Flow Modification Entry to push
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001522 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001523 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1524 .getMessage(OFType.FLOW_MOD);
1525 long cookie = flowEntryId.value();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001526
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001527 short flowModCommand = OFFlowMod.OFPFC_ADD;
1528 if (userState.equals("FE_USER_ADD")) {
1529 flowModCommand = OFFlowMod.OFPFC_ADD;
1530 } else if (userState.equals("FE_USER_MODIFY")) {
1531 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1532 } else if (userState.equals("FE_USER_DELETE")) {
1533 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1534 } else {
1535 // Unknown user state. Ignore the entry
1536 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1537 flowEntryId.toString(), userState);
1538 return false;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001539 }
1540
1541 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001542 // Fetch the match conditions.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001543 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001544 // NOTE: The Flow matching conditions common for all Flow Entries are
1545 // used ONLY if a Flow Entry does NOT have the corresponding matching
1546 // condition set.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001547 //
1548 OFMatch match = new OFMatch();
1549 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001550
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001551 // Match the Incoming Port
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001552 Short matchInPort = flowEntryObj.getMatchInPort();
1553 if (matchInPort != null) {
1554 match.setInputPort(matchInPort);
1555 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1556 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001557
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001558 // Match the Ethernet Frame Type
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001559 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1560 if (matchEthernetFrameType == null)
1561 matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1562 if (matchEthernetFrameType != null) {
1563 match.setDataLayerType(matchEthernetFrameType);
1564 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1565 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001566
1567 // Match the Source IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001568 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1569 if (matchSrcIPv4Net == null)
1570 matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1571 if (matchSrcIPv4Net != null) {
1572 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
1573 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001574
1575 // Natch the Destination IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001576 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1577 if (matchDstIPv4Net == null)
1578 matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1579 if (matchDstIPv4Net != null) {
1580 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
1581 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001582
1583 // Match the Source MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001584 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1585 if (matchSrcMac == null)
1586 matchSrcMac = flowObj.getMatchSrcMac();
1587 if (matchSrcMac != null) {
1588 match.setDataLayerSource(matchSrcMac);
1589 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1590 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001591
1592 // Match the Destination MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001593 String matchDstMac = flowEntryObj.getMatchDstMac();
1594 if (matchDstMac == null)
1595 matchDstMac = flowObj.getMatchDstMac();
1596 if (matchDstMac != null) {
1597 match.setDataLayerDestination(matchDstMac);
1598 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1599 }
1600
1601 //
1602 // Fetch the actions
1603 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001604 // TODO: For now we support only the "OUTPUT" actions.
1605 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001606 List<OFAction> actions = new ArrayList<OFAction>();
1607 Short actionOutputPort = flowEntryObj.getActionOutput();
1608 if (actionOutputPort != null) {
1609 OFActionOutput action = new OFActionOutput();
1610 // XXX: The max length is hard-coded for now
1611 action.setMaxLength((short)0xffff);
1612 action.setPort(actionOutputPort);
1613 actions.add(action);
1614 }
1615
1616 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1617 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1618 .setPriority(PRIORITY_DEFAULT)
1619 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1620 .setCookie(cookie)
1621 .setCommand(flowModCommand)
1622 .setMatch(match)
1623 .setActions(actions)
1624 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1625 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1626 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1627 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1628 if (actionOutputPort != null)
1629 fm.setOutPort(actionOutputPort);
1630 }
1631
1632 //
1633 // TODO: Set the following flag
1634 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1635 // See method ForwardingBase::pushRoute()
1636 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001637
1638 //
1639 // Write the message to the switch
1640 //
Pavlin Radoslavov20d35a22013-04-05 10:16:15 -07001641 log.debug("MEASUREMENT: Installing flow entry " + userState +
1642 " into switch DPID: " +
1643 mySwitch.getStringId() +
1644 " flowEntryId: " + flowEntryId.toString() +
1645 " srcMac: " + matchSrcMac + " dstMac: " + matchDstMac +
1646 " inPort: " + matchInPort + " outPort: " + actionOutputPort
1647 );
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001648 try {
1649 messageDamper.write(mySwitch, fm, null);
1650 mySwitch.flush();
1651 //
1652 // TODO: We should use the OpenFlow Barrier mechanism
1653 // to check for errors, and update the SwitchState
1654 // for a flow entry after the Barrier message is
1655 // is received.
1656 //
1657 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1658 } catch (IOException e) {
1659 log.error("Failure writing flow mod from network map", e);
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001660 return false;
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001661 }
1662
1663 return true;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001664 }
1665
1666 /**
1667 * Install a Flow Entry on a switch.
1668 *
1669 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001670 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001671 * @param flowEntry the flow entry to install.
1672 * @return true on success, otherwise false.
1673 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001674 public boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1675 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001676 //
1677 // Create the OpenFlow Flow Modification Entry to push
1678 //
1679 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1680 .getMessage(OFType.FLOW_MOD);
1681 long cookie = flowEntry.flowEntryId().value();
1682
1683 short flowModCommand = OFFlowMod.OFPFC_ADD;
1684 if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
1685 flowModCommand = OFFlowMod.OFPFC_ADD;
1686 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
1687 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1688 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
1689 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1690 } else {
1691 // Unknown user state. Ignore the entry
1692 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1693 flowEntry.flowEntryId().toString(),
1694 flowEntry.flowEntryUserState());
1695 return false;
1696 }
1697
1698 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001699 // Fetch the match conditions.
1700 //
1701 // NOTE: The Flow matching conditions common for all Flow Entries are
1702 // used ONLY if a Flow Entry does NOT have the corresponding matching
1703 // condition set.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001704 //
1705 OFMatch match = new OFMatch();
1706 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001707 FlowEntryMatch flowPathMatch = flowPath.flowEntryMatch();
1708 FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
1709
1710 // Match the Incoming Port
1711 Port matchInPort = flowEntryMatch.inPort();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001712 if (matchInPort != null) {
1713 match.setInputPort(matchInPort.value());
1714 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1715 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001716
1717 // Match the Ethernet Frame Type
1718 Short matchEthernetFrameType = flowEntryMatch.ethernetFrameType();
1719 if ((matchEthernetFrameType == null) && (flowPathMatch != null)) {
1720 matchEthernetFrameType = flowPathMatch.ethernetFrameType();
1721 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001722 if (matchEthernetFrameType != null) {
1723 match.setDataLayerType(matchEthernetFrameType);
1724 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1725 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001726
1727 // Match the Source IPv4 Network prefix
1728 IPv4Net matchSrcIPv4Net = flowEntryMatch.srcIPv4Net();
1729 if ((matchSrcIPv4Net == null) && (flowPathMatch != null)) {
1730 matchSrcIPv4Net = flowPathMatch.srcIPv4Net();
1731 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001732 if (matchSrcIPv4Net != null) {
1733 match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
1734 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001735
1736 // Natch the Destination IPv4 Network prefix
1737 IPv4Net matchDstIPv4Net = flowEntryMatch.dstIPv4Net();
1738 if ((matchDstIPv4Net == null) && (flowPathMatch != null)) {
1739 matchDstIPv4Net = flowPathMatch.dstIPv4Net();
1740 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001741 if (matchDstIPv4Net != null) {
1742 match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
1743 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001744
1745 // Match the Source MAC address
1746 MACAddress matchSrcMac = flowEntryMatch.srcMac();
1747 if ((matchSrcMac == null) && (flowPathMatch != null)) {
1748 matchSrcMac = flowPathMatch.srcMac();
1749 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001750 if (matchSrcMac != null) {
1751 match.setDataLayerSource(matchSrcMac.toString());
1752 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1753 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001754
1755 // Match the Destination MAC address
1756 MACAddress matchDstMac = flowEntryMatch.dstMac();
1757 if ((matchDstMac == null) && (flowPathMatch != null)) {
1758 matchDstMac = flowPathMatch.dstMac();
1759 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001760 if (matchDstMac != null) {
1761 match.setDataLayerDestination(matchDstMac.toString());
1762 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1763 }
1764
1765 //
1766 // Fetch the actions
1767 //
1768 // TODO: For now we support only the "OUTPUT" actions.
1769 //
1770 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1771 List<OFAction> actions = new ArrayList<OFAction>();
1772 ArrayList<FlowEntryAction> flowEntryActions =
1773 flowEntry.flowEntryActions();
1774 for (FlowEntryAction flowEntryAction : flowEntryActions) {
1775 FlowEntryAction.ActionOutput actionOutput =
1776 flowEntryAction.actionOutput();
1777 if (actionOutput != null) {
1778 short actionOutputPort = actionOutput.port().value();
1779 OFActionOutput action = new OFActionOutput();
1780 // XXX: The max length is hard-coded for now
1781 action.setMaxLength((short)0xffff);
1782 action.setPort(actionOutputPort);
1783 actions.add(action);
1784 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1785 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1786 fm.setOutPort(actionOutputPort);
1787 }
1788 }
1789 }
1790
1791 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1792 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1793 .setPriority(PRIORITY_DEFAULT)
1794 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1795 .setCookie(cookie)
1796 .setCommand(flowModCommand)
1797 .setMatch(match)
1798 .setActions(actions)
1799 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1800
1801 //
1802 // TODO: Set the following flag
1803 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1804 // See method ForwardingBase::pushRoute()
1805 //
1806
1807 //
1808 // Write the message to the switch
1809 //
1810 try {
1811 messageDamper.write(mySwitch, fm, null);
1812 mySwitch.flush();
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001813 //
1814 // TODO: We should use the OpenFlow Barrier mechanism
1815 // to check for errors, and update the SwitchState
1816 // for a flow entry after the Barrier message is
1817 // is received.
1818 //
1819 // TODO: The FlowEntry Object in Titan should be set
1820 // to FE_SWITCH_UPDATED.
1821 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001822 } catch (IOException e) {
1823 log.error("Failure writing flow mod from network map", e);
1824 return false;
1825 }
1826 return true;
1827 }
1828
1829 /**
1830 * Remove a Flow Entry from a switch.
1831 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001832 * @param mySwitch the switch to remove the Flow Entry from.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001833 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001834 * @param flowEntry the flow entry to remove.
1835 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001836 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001837 public boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1838 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001839 //
1840 // The installFlowEntry() method implements both installation
1841 // and removal of flow entries.
1842 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001843 return (installFlowEntry(mySwitch, flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001844 }
1845
1846 /**
1847 * Install a Flow Entry on a remote controller.
1848 *
1849 * TODO: We need it now: Jono
1850 * - For now it will make a REST call to the remote controller.
1851 * - Internally, it needs to know the name of the remote controller.
1852 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001853 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001854 * @param flowEntry the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001855 * @return true on success, otherwise false.
1856 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001857 public boolean installRemoteFlowEntry(FlowPath flowPath,
1858 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001859 // TODO: We need it now: Jono
1860 // - For now it will make a REST call to the remote controller.
1861 // - Internally, it needs to know the name of the remote controller.
1862 return true;
1863 }
1864
1865 /**
1866 * Remove a flow entry on a remote controller.
1867 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001868 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001869 * @param flowEntry the flow entry to remove.
1870 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001871 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001872 public boolean removeRemoteFlowEntry(FlowPath flowPath,
1873 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001874 //
1875 // The installRemoteFlowEntry() method implements both installation
1876 // and removal of flow entries.
1877 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001878 return (installRemoteFlowEntry(flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001879 }
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001880
1881 /**
1882 * Store a path flow for measurement purpose.
1883 *
1884 * NOTE: The Flow Path argument does NOT contain flow entries.
1885 * The Shortest Path is computed, and the corresponding Flow Entries
1886 * are stored in the Flow Path.
1887 *
1888 * @param flowPath the Flow Path with the endpoints and the match
1889 * conditions to store.
1890 * @return the stored shortest-path flow on success, otherwise null.
1891 */
1892 @Override
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001893 public synchronized FlowPath measurementStorePathFlow(FlowPath flowPath) {
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001894 //
1895 // Prepare the Shortest Path computation if the first Flow Path
1896 //
1897 if (measurementStoredPaths.isEmpty())
Pavlin Radoslavov9556b142013-05-20 21:49:04 +00001898 measurementShortestPathTopo = topoRouteService.prepareShortestPathTopo();
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001899
1900 //
1901 // Compute the Shortest Path
1902 //
1903 DataPath dataPath =
Pavlin Radoslavov9556b142013-05-20 21:49:04 +00001904 topoRouteService.getTopoShortestPath(measurementShortestPathTopo,
1905 flowPath.dataPath().srcPort(),
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001906 flowPath.dataPath().dstPort());
1907 if (dataPath == null) {
1908 // We need the DataPath to populate the Network MAP
1909 dataPath = new DataPath();
1910 dataPath.setSrcPort(flowPath.dataPath().srcPort());
1911 dataPath.setDstPort(flowPath.dataPath().dstPort());
1912 }
1913
1914 //
1915 // Set the incoming port matching and the outgoing port output
1916 // actions for each flow entry.
1917 //
1918 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1919 // Set the incoming port matching
1920 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
1921 flowEntry.setFlowEntryMatch(flowEntryMatch);
1922 flowEntryMatch.enableInPort(flowEntry.inPort());
1923
1924 // Set the outgoing port output action
1925 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1926 if (flowEntryActions == null) {
1927 flowEntryActions = new ArrayList<FlowEntryAction>();
1928 flowEntry.setFlowEntryActions(flowEntryActions);
1929 }
1930 FlowEntryAction flowEntryAction = new FlowEntryAction();
1931 flowEntryAction.setActionOutput(flowEntry.outPort());
1932 flowEntryActions.add(flowEntryAction);
1933 }
1934
1935 //
1936 // Prepare the computed Flow Path
1937 //
1938 FlowPath computedFlowPath = new FlowPath();
1939 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1940 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1941 computedFlowPath.setDataPath(dataPath);
1942 computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
1943
1944 //
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001945 // Add the computed Flow Path to the internal storage
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001946 //
1947 measurementStoredPaths.add(computedFlowPath);
1948
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001949 log.debug("Measurement storing path {}",
1950 computedFlowPath.flowId().toString());
1951
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07001952 return (computedFlowPath);
1953 }
1954
1955 /**
1956 * Install path flows for measurement purpose.
1957 *
1958 * @param numThreads the number of threads to use to install the path
1959 * flows.
1960 * @return true on success, otherwise false.
1961 */
1962 @Override
1963 public boolean measurementInstallPaths(Integer numThreads) {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001964 // Create a copy of the Flow Paths to install
1965 final ConcurrentLinkedQueue<FlowPath> measurementProcessingPaths =
1966 new ConcurrentLinkedQueue<FlowPath>(measurementStoredPaths);
1967
Pavlin Radoslavovf5d80412013-05-24 05:14:07 +00001968 /**
1969 * A Thread-wrapper class for executing the threads and collecting
1970 * the measurement data.
1971 */
1972 class MyThread extends Thread {
1973 public long[] execTime = new long[2000];
1974 public int samples = 0;
1975 public int threadId = -1;
1976 @Override
1977 public void run() {
1978 while (true) {
1979 FlowPath flowPath = measurementProcessingPaths.poll();
1980 if (flowPath == null)
1981 return;
1982 // Install the Flow Path
1983 FlowId flowId = new FlowId();
1984 String dataPathSummaryStr =
1985 flowPath.dataPath().dataPathSummary();
1986 long startTime = System.nanoTime();
1987 addFlow(flowPath, flowId, dataPathSummaryStr);
1988 long endTime = System.nanoTime();
1989 execTime[samples] = endTime - startTime;
1990 samples++;
1991 }
1992 }
1993 };
1994
1995 List<MyThread> threads = new LinkedList<MyThread>();
1996
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00001997 log.debug("Measurement Installing {} flows",
1998 measurementProcessingPaths.size());
Pavlin Radoslavove0938f32013-05-07 23:17:22 +00001999
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002000 //
2001 // Create the threads to install the Flow Paths
2002 //
2003 for (int i = 0; i < numThreads; i++) {
Pavlin Radoslavovf5d80412013-05-24 05:14:07 +00002004 MyThread thread = new MyThread();
2005 thread.threadId = i;
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002006 threads.add(thread);
2007 }
2008
2009 //
2010 // Start processing
2011 //
2012 measurementEndTimeProcessingPaths = 0;
2013 measurementStartTimeProcessingPaths = System.nanoTime();
2014 for (Thread thread : threads) {
2015 thread.start();
2016 }
2017
Pavlin Radoslavovf5d80412013-05-24 05:14:07 +00002018 // Wait for all threads to complete
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00002019 for (Thread thread : threads) {
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002020 try {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00002021 thread.join();
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002022 } catch (InterruptedException e) {
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00002023 log.debug("Exception waiting for a thread to install a Flow Path: ", e);
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002024 }
2025 }
2026
Pavlin Radoslavov759772f2013-05-20 20:50:00 +00002027 // Record the end of processing
2028 measurementEndTimeProcessingPaths = System.nanoTime();
2029
Pavlin Radoslavovf5d80412013-05-24 05:14:07 +00002030 //
2031 // Prepare the string with measurement data per each Flow Path
2032 // installation.
2033 // The string is multiple lines: one line per Flow Path installation:
2034 // ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
2035 //
2036 measurementPerFlowStr = new String();
2037 String eol = System.getProperty("line.separator");
2038 for (MyThread thread : threads) {
2039 for (int i = 0; i < thread.samples; i++) {
2040 measurementPerFlowStr += "ThreadAndTimePerFlow " + thread.threadId + " " + numThreads + " " + thread.execTime[i] + eol;
2041 }
2042 }
2043
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002044 return true;
2045 }
2046
2047 /**
2048 * Get the measurement time that took to install the path flows.
2049 *
2050 * @return the measurement time (in nanoseconds) it took to install
2051 * the path flows.
2052 */
2053 @Override
2054 public Long measurementGetInstallPathsTimeNsec() {
2055 return new Long(measurementEndTimeProcessingPaths -
2056 measurementStartTimeProcessingPaths);
2057 }
2058
2059 /**
Pavlin Radoslavovf5d80412013-05-24 05:14:07 +00002060 * Get the measurement install time per Flow.
2061 *
2062 * @return a multi-line string with the following format per line:
2063 * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
2064 */
2065 @Override
2066 public String measurementGetPerFlowInstallTime() {
2067 return new String(measurementPerFlowStr);
2068 }
2069
2070 /**
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002071 * Clear the path flows stored for measurement purpose.
2072 *
2073 * @return true on success, otherwise false.
2074 */
2075 @Override
2076 public boolean measurementClearAllPaths() {
2077 measurementStoredPaths.clear();
Pavlin Radoslavov9556b142013-05-20 21:49:04 +00002078 topoRouteService.dropShortestPathTopo(measurementShortestPathTopo);
Pavlin Radoslavove0938f32013-05-07 23:17:22 +00002079 measurementStartTimeProcessingPaths = 0;
2080 measurementEndTimeProcessingPaths = 0;
Pavlin Radoslavovf5d80412013-05-24 05:14:07 +00002081 measurementPerFlowStr = new String();
Pavlin Radoslavov4ef543e2013-05-07 13:36:57 -07002082
2083 return true;
2084 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08002085}