blob: 5c263c2122fb049690a0746b0011da6b40292381 [file] [log] [blame]
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001package net.floodlightcontroller.flowcache;
2
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08003import java.io.IOException;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08004import java.util.ArrayList;
5import java.util.Collection;
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +00006import java.util.Collections;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08007import java.util.EnumSet;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08008import java.util.HashMap;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07009import java.util.HashSet;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +000010import java.util.LinkedList;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080011import java.util.List;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080012import java.util.Map;
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +000013import java.util.Random;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070014import java.util.TreeMap;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070015import java.util.concurrent.BlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080016import java.util.concurrent.Executors;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070017import java.util.concurrent.LinkedBlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080018import java.util.concurrent.ScheduledExecutorService;
19import java.util.concurrent.ScheduledFuture;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070020import java.util.concurrent.ThreadPoolExecutor;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080021import java.util.concurrent.TimeUnit;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080022
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080023import net.floodlightcontroller.core.IFloodlightProviderService;
24import net.floodlightcontroller.core.INetMapStorage;
25import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
26import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070027import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
Pankaj Berded0079742013-03-27 17:53:25 -070028import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070029import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080030import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080031import net.floodlightcontroller.core.module.FloodlightModuleContext;
32import net.floodlightcontroller.core.module.FloodlightModuleException;
33import net.floodlightcontroller.core.module.IFloodlightModule;
34import net.floodlightcontroller.core.module.IFloodlightService;
35import net.floodlightcontroller.flowcache.IFlowService;
36import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
37import net.floodlightcontroller.restserver.IRestApiService;
38import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080039import net.floodlightcontroller.util.DataPath;
40import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080041import net.floodlightcontroller.util.DataPathEndpoints;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080042import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070043import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080044import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070045import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080046import net.floodlightcontroller.util.FlowEntrySwitchState;
47import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080048import net.floodlightcontroller.util.FlowId;
49import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070050import net.floodlightcontroller.util.IPv4Net;
51import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080052import net.floodlightcontroller.util.OFMessageDamper;
53import net.floodlightcontroller.util.Port;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070054import net.floodlightcontroller.util.SwitchPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080055import net.onrc.onos.util.GraphDBConnection;
56import net.onrc.onos.util.GraphDBConnection.Transaction;
57
58import org.openflow.protocol.OFFlowMod;
59import org.openflow.protocol.OFMatch;
60import org.openflow.protocol.OFPacketOut;
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070061import org.openflow.protocol.OFPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080062import org.openflow.protocol.OFType;
63import org.openflow.protocol.action.OFAction;
64import org.openflow.protocol.action.OFActionOutput;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080065
66import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
68
Pavlin Radoslavov5adf1522013-04-04 17:43:41 -070069public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080070
71 public GraphDBConnection conn;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080072
73 protected IRestApiService restApi;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080074 protected IFloodlightProviderService floodlightProvider;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070075 protected ITopoRouteService topoRouteService;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070076 protected FloodlightModuleContext context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080077
78 protected OFMessageDamper messageDamper;
79
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070080 //
81 // TODO: Values copied from elsewhere (class LearningSwitch).
82 // The local copy should go away!
83 //
84 protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
85 protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
86 public static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0; // infinity
87 public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
88 public static final short PRIORITY_DEFAULT = 100;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080089
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +000090 // Flow Entry ID generation state
91 private static Random randomGenerator = new Random();
92 private static int nextFlowEntryIdPrefix = 0;
93 private static int nextFlowEntryIdSuffix = 0;
94 private static long nextFlowEntryId = 0;
95
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070096 private static long measurementFlowId = 100000;
97 private static String measurementFlowIdStr = "0x186a0"; // 100000
98 private long modifiedMeasurementFlowTime = 0;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070099
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800100 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800101 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
102
103 // The periodic task(s)
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700104 private final ScheduledExecutorService mapReaderScheduler =
105 Executors.newScheduledThreadPool(1);
106
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700107 final Runnable mapReader = new Runnable() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800108 public void run() {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700109 long startTime = System.nanoTime();
110 int counterAllFlowEntries = 0;
111 int counterMyNotUpdatedFlowEntries = 0;
112 int counterAllFlowPaths = 0;
113 int counterMyFlowPaths = 0;
114
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800115 if (floodlightProvider == null) {
116 log.debug("FloodlightProvider service not found!");
117 return;
118 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000119 Map<Long, IOFSwitch> mySwitches =
120 floodlightProvider.getSwitches();
121 Map<Long, IFlowEntry> myFlowEntries =
122 new TreeMap<Long, IFlowEntry>();
123 LinkedList<IFlowEntry> deleteFlowEntries =
124 new LinkedList<IFlowEntry>();
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700125
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700126
127 //
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700128 // Fetch all Flow Entries and select only my Flow Entries
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700129 // that need to be updated into the switches.
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700130 //
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000131 Iterable<IFlowEntry> allFlowEntries =
132 conn.utils().getAllFlowEntries(conn);
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700133 for (IFlowEntry flowEntryObj : allFlowEntries) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700134 counterAllFlowEntries++;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000135 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700136 String userState = flowEntryObj.getUserState();
137 String switchState = flowEntryObj.getSwitchState();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000138 String dpidStr = flowEntryObj.getSwitchDpid();
139 if ((flowEntryIdStr == null) ||
140 (userState == null) ||
141 (switchState == null) ||
142 (dpidStr == null)) {
143 log.debug("IGNORING Flow Entry entry with null fields");
144 continue;
145 }
146 FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
147 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800148
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000149 if (! switchState.equals("FE_SWITCH_NOT_UPDATED"))
150 continue; // Ignore the entry: nothing to do
151
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800152 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000153 if (mySwitch == null)
154 continue; // Ignore the entry: not my switch
155
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700156 myFlowEntries.put(flowEntryId.value(), flowEntryObj);
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700157 if (userState.equals("FE_USER_DELETE")) {
158 // An entry that needs to be deleted.
159 deleteFlowEntries.add(flowEntryObj);
160 }
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700161 }
162
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700163 log.debug("MEASUREMENT: Found {} My Flow Entries NOT_UPDATED",
164 myFlowEntries.size());
165
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700166 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700167 // Process my Flow Entries in the Flow Entry ID order
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700168 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700169 boolean processed_measurement_flow = false;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700170 for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700171 counterMyNotUpdatedFlowEntries++;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700172 IFlowEntry flowEntryObj = entry.getValue();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700173 IFlowPath flowObj =
174 conn.utils().getFlowPathByFlowEntry(conn,
175 flowEntryObj);
176 if (flowObj == null)
177 continue; // Should NOT happen
178
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700179 // Code for measurement purpose
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700180 // TODO: Commented-out for now
181 /*
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700182 {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700183 if (flowObj.getFlowId().equals(measurementFlowIdStr)) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700184 processed_measurement_flow = true;
185 }
186 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700187 */
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700188
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700189 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700190 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000191 if (mySwitch == null)
192 continue; // Shouldn't happen
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700193 installFlowEntry(mySwitch, flowObj, flowEntryObj);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800194 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000195
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700196 log.debug("MEASUREMENT: Found {} Flow Entries to delete",
197 deleteFlowEntries.size());
198
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000199 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700200 // Delete all entries marked for deletion from the
201 // Network MAP.
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000202 //
203 // TODO: We should use the OpenFlow Barrier mechanism
204 // to check for errors, and delete the Flow Entries after the
205 // Barrier message is received.
206 //
207 while (! deleteFlowEntries.isEmpty()) {
208 IFlowEntry flowEntryObj = deleteFlowEntries.poll();
209 IFlowPath flowObj =
210 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
211 if (flowObj == null) {
212 log.debug("Did not find FlowPath to be deleted");
213 continue;
214 }
215 flowObj.removeFlowEntry(flowEntryObj);
216 conn.utils().removeFlowEntry(conn, flowEntryObj);
217
218 // Test whether the last flow entry
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700219 Iterable<IFlowEntry> tmpFlowEntries =
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000220 flowObj.getFlowEntries();
221 boolean found = false;
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700222 for (IFlowEntry tmpFlowEntryObj : tmpflowEntries) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000223 found = true;
224 break;
225 }
226 if (! found) {
227 // Remove the Flow Path as well
228 conn.utils().removeFlowPath(conn, flowObj);
229 }
230 }
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700231
232
233 //
234 // Fetch and recompute the Shortest Path for those
235 // Flow Paths this controller is responsible for.
236 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700237 topoRouteService.prepareShortestPathTopo();
238 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
239 HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
240 for (IFlowPath flowPathObj : allFlowPaths) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700241 counterAllFlowPaths++;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700242 if (flowPathObj == null)
243 continue;
244 String dataPathSummaryStr = flowPathObj.getDataPathSummary();
245 if (dataPathSummaryStr == null)
246 continue; // Could be invalid entry?
247 if (dataPathSummaryStr.isEmpty())
248 continue; // No need to maintain this flow
249
250 // Fetch the fields needed to recompute the shortest path
251 String flowIdStr = flowPathObj.getFlowId();
252 String srcDpidStr = flowPathObj.getSrcSwitch();
253 Short srcPortShort = flowPathObj.getSrcPort();
254 String dstDpidStr = flowPathObj.getDstSwitch();
255 Short dstPortShort = flowPathObj.getDstPort();
256 if ((flowIdStr == null) ||
257 (srcDpidStr == null) ||
258 (srcPortShort == null) ||
259 (dstDpidStr == null) ||
260 (dstPortShort == null)) {
261 log.debug("IGNORING Flow Path entry with null fields");
262 continue;
263 }
264
265 FlowId flowId = new FlowId(flowIdStr);
266 Dpid srcDpid = new Dpid(srcDpidStr);
267 Port srcPort = new Port(srcPortShort);
268 Dpid dstDpid = new Dpid(dstDpidStr);
269 Port dstPort = new Port(dstPortShort);
270 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
271 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
Pavlin Radoslavov2659a0b2013-04-03 20:30:40 -0700272
273 //
274 // Use the source DPID as a heuristic to decide
275 // which controller is responsible for maintaining the
276 // shortest path.
277 // NOTE: This heuristic is error-prone: if the switch
278 // goes away and no controller is responsible for that
279 // switch, then the original Flow Path is not cleaned-up
280 //
281 IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
282 if (mySwitch == null)
283 continue; // Ignore: not my responsibility
284
285 counterMyFlowPaths++;
286
Pavlin Radoslavov832aa652013-03-29 16:21:59 -0700287 //
288 // NOTE: Using here the regular getShortestPath() method
289 // won't work here, because that method calls internally
290 // "conn.endTx(Transaction.COMMIT)", and that will
291 // invalidate all handlers to the Titan database.
292 // If we want to experiment with calling here
293 // getShortestPath(), we need to refactor that code
294 // to avoid closing the transaction.
295 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700296 DataPath dataPath =
297 topoRouteService.getTopoShortestPath(srcSwitchPort,
298 dstSwitchPort);
Pavlin Radoslavov4a325822013-04-02 22:16:59 +0000299 if (dataPath == null) {
300 // We need the DataPath to compare the paths
301 dataPath = new DataPath();
302 dataPath.setSrcPort(srcSwitchPort);
303 dataPath.setDstPort(dstSwitchPort);
304 }
305
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700306 String newDataPathSummaryStr = dataPath.dataPathSummary();
307 if (dataPathSummaryStr.equals(newDataPathSummaryStr))
308 continue; // Nothing changed
309
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700310 log.debug("RECONCILE: Need to Reconcile Shortest Path for FlowID {}",
311 flowId.toString());
312 flowObjSet.add(flowPathObj);
313 }
Pavlin Radoslavov53a3a8c2013-04-04 04:34:50 -0700314 log.debug("MEASUREMENT: Found {} Flows to reconcile",
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700315 flowObjSet.size());
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700316 reconcileFlows(flowObjSet);
317 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700318
319
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800320 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700321
322 if (processed_measurement_flow) {
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700323 long estimatedTime =
324 System.nanoTime() - modifiedMeasurementFlowTime;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700325 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
326 (double)estimatedTime / 1000000000 + " sec";
327 log.debug(logMsg);
328 }
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700329
330 long estimatedTime = System.nanoTime() - startTime;
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700331 double rate = 0.0;
332 if (estimatedTime > 0)
333 rate = ((double)counterAllFlowPaths * 1000000000) / estimatedTime;
334 String logMsg = "MEASUREMENT: Processed AllFlowEntries: " +
335 counterAllFlowEntries + " MyNotUpdatedFlowEntries: " +
336 counterMyNotUpdatedFlowEntries + " AllFlowPaths: " +
337 counterAllFlowPaths + " MyFlowPaths: " +
338 counterMyFlowPaths + " in " +
339 (double)estimatedTime / 1000000000 + " sec: " +
340 rate + " paths/s";
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700341 log.debug(logMsg);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800342 }
343 };
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700344
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700345 final ScheduledFuture<?> mapReaderHandle =
346 mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800347
348 @Override
349 public void init(String conf) {
350 conn = GraphDBConnection.getInstance(conf);
351 }
352
353 public void finalize() {
354 close();
355 }
356
357 @Override
358 public void close() {
359 conn.close();
360 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800361
362 @Override
363 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
364 Collection<Class<? extends IFloodlightService>> l =
365 new ArrayList<Class<? extends IFloodlightService>>();
366 l.add(IFlowService.class);
367 return l;
368 }
369
370 @Override
371 public Map<Class<? extends IFloodlightService>, IFloodlightService>
372 getServiceImpls() {
373 Map<Class<? extends IFloodlightService>,
374 IFloodlightService> m =
375 new HashMap<Class<? extends IFloodlightService>,
376 IFloodlightService>();
377 m.put(IFlowService.class, this);
378 return m;
379 }
380
381 @Override
382 public Collection<Class<? extends IFloodlightService>>
383 getModuleDependencies() {
384 Collection<Class<? extends IFloodlightService>> l =
385 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800386 l.add(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700387 l.add(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800388 l.add(IRestApiService.class);
389 return l;
390 }
391
392 @Override
393 public void init(FloodlightModuleContext context)
394 throws FloodlightModuleException {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700395 this.context = context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800396 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700397 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800398 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800399 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
400 EnumSet.of(OFType.FLOW_MOD),
401 OFMESSAGE_DAMPER_TIMEOUT);
402 // TODO: An ugly hack!
403 String conf = "/tmp/cassandra.titan";
404 this.init(conf);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800405 }
406
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000407 private long getNextFlowEntryId() {
408 //
409 // Generate the next Flow Entry ID.
410 // NOTE: For now, the higher 32 bits are random, and
411 // the lower 32 bits are sequential.
412 // In the future, we need a better allocation mechanism.
413 //
414 if ((nextFlowEntryIdSuffix & 0xffffffffL) == 0xffffffffL) {
415 nextFlowEntryIdPrefix = randomGenerator.nextInt();
416 nextFlowEntryIdSuffix = 0;
417 } else {
418 nextFlowEntryIdSuffix++;
419 }
420 long result = (long)nextFlowEntryIdPrefix << 32;
421 result = result | (0xffffffffL & nextFlowEntryIdSuffix);
422 return result;
423 }
424
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800425 @Override
426 public void startUp(FloodlightModuleContext context) {
427 restApi.addRestletRoutable(new FlowWebRoutable());
Pavlin Radoslavov80ca6302013-03-20 02:08:09 -0700428
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000429 // Initialize the Flow Entry ID generator
430 nextFlowEntryIdPrefix = randomGenerator.nextInt();
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800431 }
432
433 /**
434 * Add a flow.
435 *
436 * Internally, ONOS will automatically register the installer for
437 * receiving Flow Path Notifications for that path.
438 *
439 * @param flowPath the Flow Path to install.
440 * @param flowId the return-by-reference Flow ID as assigned internally.
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700441 * @param dataPathSummaryStr the data path summary string if the added
442 * flow will be maintained internally, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800443 * @return true on success, otherwise false.
444 */
445 @Override
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700446 public boolean addFlow(FlowPath flowPath, FlowId flowId,
447 String dataPathSummaryStr) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700448 /*
449 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700450 if (flowPath.flowId().value() == measurementFlowId) {
451 modifiedMeasurementFlowTime = System.nanoTime();
452 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700453 */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800454
455 //
456 // Assign the FlowEntry IDs
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700457 // Right now every new flow entry gets a new flow entry ID
458 // TODO: This needs to be redesigned!
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800459 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800460 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000461 long id = getNextFlowEntryId();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800462 flowEntry.setFlowEntryId(new FlowEntryId(id));
463 }
464
465 IFlowPath flowObj = null;
466 try {
467 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
468 != null) {
469 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
470 flowPath.flowId().toString());
471 } else {
472 flowObj = conn.utils().newFlowPath(conn);
473 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
474 flowPath.flowId().toString());
475 }
476 } catch (Exception e) {
477 // TODO: handle exceptions
478 conn.endTx(Transaction.ROLLBACK);
479 log.error(":addFlow FlowId:{} failed",
480 flowPath.flowId().toString());
481 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700482 if (flowObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000483 log.error(":addFlow FlowId:{} failed: Flow object not created",
484 flowPath.flowId().toString());
485 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800486 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700487 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800488
489 //
490 // Set the Flow key:
491 // - flowId
492 //
493 flowObj.setFlowId(flowPath.flowId().toString());
494 flowObj.setType("flow");
495
496 //
497 // Set the Flow attributes:
498 // - flowPath.installerId()
499 // - flowPath.dataPath().srcPort()
500 // - flowPath.dataPath().dstPort()
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700501 // - flowPath.matchEthernetFrameType()
502 // - flowPath.matchSrcIPv4Net()
503 // - flowPath.matchDstIPv4Net()
504 // - flowPath.matchSrcMac()
505 // - flowPath.matchDstMac()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800506 //
507 flowObj.setInstallerId(flowPath.installerId().toString());
508 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
509 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
510 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
511 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700512 if (flowPath.flowEntryMatch().matchEthernetFrameType()) {
513 flowObj.setMatchEthernetFrameType(flowPath.flowEntryMatch().ethernetFrameType());
514 }
515 if (flowPath.flowEntryMatch().matchSrcIPv4Net()) {
516 flowObj.setMatchSrcIPv4Net(flowPath.flowEntryMatch().srcIPv4Net().toString());
517 }
518 if (flowPath.flowEntryMatch().matchDstIPv4Net()) {
519 flowObj.setMatchDstIPv4Net(flowPath.flowEntryMatch().dstIPv4Net().toString());
520 }
521 if (flowPath.flowEntryMatch().matchSrcMac()) {
522 flowObj.setMatchSrcMac(flowPath.flowEntryMatch().srcMac().toString());
523 }
524 if (flowPath.flowEntryMatch().matchDstMac()) {
525 flowObj.setMatchDstMac(flowPath.flowEntryMatch().dstMac().toString());
526 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800527
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700528 if (dataPathSummaryStr != null) {
529 flowObj.setDataPathSummary(dataPathSummaryStr);
530 } else {
531 flowObj.setDataPathSummary("");
532 }
533
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800534 // Flow edges:
535 // HeadFE
536
537
538 //
539 // Flow Entries:
540 // flowPath.dataPath().flowEntries()
541 //
542 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
543 IFlowEntry flowEntryObj = null;
544 boolean found = false;
545 try {
546 if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
547 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
548 flowEntry.flowEntryId().toString());
549 found = true;
550 } else {
551 flowEntryObj = conn.utils().newFlowEntry(conn);
552 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
553 flowEntry.flowEntryId().toString());
554 }
555 } catch (Exception e) {
556 // TODO: handle exceptions
557 conn.endTx(Transaction.ROLLBACK);
558 log.error(":addFlow FlowEntryId:{} failed",
559 flowEntry.flowEntryId().toString());
560 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700561 if (flowEntryObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000562 log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created",
563 flowEntry.flowEntryId().toString());
564 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800565 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700566 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800567
568 //
569 // Set the Flow Entry key:
570 // - flowEntry.flowEntryId()
571 //
572 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
573 flowEntryObj.setType("flow_entry");
574
575 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700576 // Set the Flow Entry Edges and attributes:
577 // - Switch edge
578 // - InPort edge
579 // - OutPort edge
580 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800581 // - flowEntry.flowEntryMatch()
582 // - flowEntry.flowEntryActions()
583 // - flowEntry.dpid()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800584 // - flowEntry.flowEntryUserState()
585 // - flowEntry.flowEntrySwitchState()
586 // - flowEntry.flowEntryErrorState()
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700587 // - flowEntry.matchInPort()
588 // - flowEntry.matchEthernetFrameType()
589 // - flowEntry.matchSrcIPv4Net()
590 // - flowEntry.matchDstIPv4Net()
591 // - flowEntry.matchSrcMac()
592 // - flowEntry.matchDstMac()
593 // - flowEntry.actionOutput()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800594 //
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700595 ISwitchObject sw =
596 conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800597 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
Pankaj Berded0079742013-03-27 17:53:25 -0700598 flowEntryObj.setSwitch(sw);
599 if (flowEntry.flowEntryMatch().matchInPort()) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700600 IPortObject inport =
601 conn.utils().searchPort(conn, flowEntry.dpid().toString(),
602 flowEntry.flowEntryMatch().inPort().value());
Pankaj Berded0079742013-03-27 17:53:25 -0700603 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
604 flowEntryObj.setInPort(inport);
605 }
606 if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
607 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
608 }
609 if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
610 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
611 }
612 if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
613 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
614 }
615 if (flowEntry.flowEntryMatch().matchSrcMac()) {
616 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
617 }
618 if (flowEntry.flowEntryMatch().matchDstMac()) {
619 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
620 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700621
622 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
Pankaj Berded0079742013-03-27 17:53:25 -0700623 if (fa.actionOutput() != null) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700624 IPortObject outport =
625 conn.utils().searchPort(conn,
626 flowEntry.dpid().toString(),
627 fa.actionOutput().port().value());
628 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
629 flowEntryObj.setOutPort(outport);
Pankaj Berded0079742013-03-27 17:53:25 -0700630 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700631 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800632 // TODO: Hacks with hard-coded state names!
633 if (found)
634 flowEntryObj.setUserState("FE_USER_MODIFY");
635 else
636 flowEntryObj.setUserState("FE_USER_ADD");
637 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
638 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700639 // TODO: Take care of the FlowEntryErrorState.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800640 //
641
642 // Flow Entries edges:
643 // Flow
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700644 // NextFE (TODO)
645 if (! found) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800646 flowObj.addFlowEntry(flowEntryObj);
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700647 flowEntryObj.setFlow(flowObj);
648 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800649 }
650 conn.endTx(Transaction.COMMIT);
651
652 //
653 // TODO: We need a proper Flow ID allocation mechanism.
654 //
655 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700656
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800657 return true;
658 }
659
660 /**
661 * Delete a previously added flow.
662 *
663 * @param flowId the Flow ID of the flow to delete.
664 * @return true on success, otherwise false.
665 */
666 @Override
667 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700668 /*
669 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700670 if (flowId.value() == measurementFlowId) {
671 modifiedMeasurementFlowTime = System.nanoTime();
672 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700673 */
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700674
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800675 IFlowPath flowObj = null;
676 //
677 // We just mark the entries for deletion,
678 // and let the switches remove each individual entry after
679 // it has been removed from the switches.
680 //
681 try {
682 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
683 != null) {
684 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
685 flowId.toString());
686 } else {
687 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
688 flowId.toString());
689 }
690 } catch (Exception e) {
691 // TODO: handle exceptions
692 conn.endTx(Transaction.ROLLBACK);
693 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
694 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700695 if (flowObj == null) {
696 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800697 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700698 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800699
700 //
701 // Find and mark for deletion all Flow Entries
702 //
703 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
704 boolean empty = true; // TODO: an ugly hack
705 for (IFlowEntry flowEntryObj : flowEntries) {
706 empty = false;
707 // flowObj.removeFlowEntry(flowEntryObj);
708 // conn.utils().removeFlowEntry(conn, flowEntryObj);
709 flowEntryObj.setUserState("FE_USER_DELETE");
710 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
711 }
712 // Remove from the database empty flows
713 if (empty)
714 conn.utils().removeFlowPath(conn, flowObj);
715 conn.endTx(Transaction.COMMIT);
716
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800717 return true;
718 }
719
720 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700721 * Clear the state for a previously added flow.
722 *
723 * @param flowId the Flow ID of the flow to clear.
724 * @return true on success, otherwise false.
725 */
726 @Override
727 public boolean clearFlow(FlowId flowId) {
728 IFlowPath flowObj = null;
729 try {
730 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
731 != null) {
732 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
733 flowId.toString());
734 } else {
735 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
736 flowId.toString());
737 }
738 } catch (Exception e) {
739 // TODO: handle exceptions
740 conn.endTx(Transaction.ROLLBACK);
741 log.error(":clearFlow FlowId:{} failed", flowId.toString());
742 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700743 if (flowObj == null) {
744 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700745 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700746 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700747
748 //
749 // Remove all Flow Entries
750 //
751 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
752 for (IFlowEntry flowEntryObj : flowEntries) {
753 flowObj.removeFlowEntry(flowEntryObj);
754 conn.utils().removeFlowEntry(conn, flowEntryObj);
755 }
756 // Remove the Flow itself
757 conn.utils().removeFlowPath(conn, flowObj);
758 conn.endTx(Transaction.COMMIT);
759
760 return true;
761 }
762
763 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800764 * Get a previously added flow.
765 *
766 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800767 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800768 */
769 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800770 public FlowPath getFlow(FlowId flowId) {
771 IFlowPath flowObj = null;
772 try {
773 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
774 != null) {
775 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
776 flowId.toString());
777 } else {
778 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
779 flowId.toString());
780 }
781 } catch (Exception e) {
782 // TODO: handle exceptions
783 conn.endTx(Transaction.ROLLBACK);
784 log.error(":getFlow FlowId:{} failed", flowId.toString());
785 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700786 if (flowObj == null) {
787 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800788 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700789 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800790
791 //
792 // Extract the Flow state
793 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800794 FlowPath flowPath = extractFlowPath(flowObj);
795 conn.endTx(Transaction.COMMIT);
796
797 return flowPath;
798 }
799
800 /**
801 * Get all previously added flows by a specific installer for a given
802 * data path endpoints.
803 *
804 * @param installerId the Caller ID of the installer of the flow to get.
805 * @param dataPathEndpoints the data path endpoints of the flow to get.
806 * @return the Flow Paths if found, otherwise null.
807 */
808 @Override
809 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
810 DataPathEndpoints dataPathEndpoints) {
811 //
812 // TODO: The implementation below is not optimal:
813 // We fetch all flows, and then return only the subset that match
814 // the query conditions.
815 // We should use the appropriate Titan/Gremlin query to filter-out
816 // the flows as appropriate.
817 //
818 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700819 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800820
821 if (allFlows == null) {
822 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700823 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800824 }
825
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800826 for (FlowPath flow : allFlows) {
827 //
828 // TODO: String-based comparison is sub-optimal.
829 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800830 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800831 //
832 if (! flow.installerId().toString().equals(installerId.toString()))
833 continue;
834 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
835 continue;
836 }
837 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
838 continue;
839 }
840 flowPaths.add(flow);
841 }
842
843 if (flowPaths.isEmpty()) {
844 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800845 } else {
846 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
847 }
848
849 return flowPaths;
850 }
851
852 /**
853 * Get all installed flows by all installers for given data path endpoints.
854 *
855 * @param dataPathEndpoints the data path endpoints of the flows to get.
856 * @return the Flow Paths if found, otherwise null.
857 */
858 @Override
859 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
860 //
861 // TODO: The implementation below is not optimal:
862 // We fetch all flows, and then return only the subset that match
863 // the query conditions.
864 // We should use the appropriate Titan/Gremlin query to filter-out
865 // the flows as appropriate.
866 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700867 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
868 ArrayList<FlowPath> allFlows = getAllFlows();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800869
870 if (allFlows == null) {
871 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700872 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800873 }
874
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800875 for (FlowPath flow : allFlows) {
876 //
877 // TODO: String-based comparison is sub-optimal.
878 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800879 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800880 //
881 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
882 continue;
883 }
884 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
885 continue;
886 }
887 flowPaths.add(flow);
888 }
889
890 if (flowPaths.isEmpty()) {
891 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800892 } else {
893 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
894 }
895
896 return flowPaths;
897 }
898
899 /**
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700900 * Get summary of all installed flows by all installers in a given range
901 *
902 * @param flowId the data path endpoints of the flows to get.
903 * @param maxFlows: the maximum number of flows to be returned
904 * @return the Flow Paths if found, otherwise null.
905 */
906 @Override
907 public ArrayList<FlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
908 //
909 // TODO: The implementation below is not optimal:
910 // We fetch all flows, and then return only the subset that match
911 // the query conditions.
912 // We should use the appropriate Titan/Gremlin query to filter-out
913 // the flows as appropriate.
914 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700915 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
916
917 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700918
919 if (allFlows == null) {
920 log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700921 return flowPaths;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700922 }
923
Umesh Krishnaswamy244b4ae2013-03-29 12:05:15 -0700924 Collections.sort(allFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700925
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700926 for (FlowPath flow : allFlows) {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700927 flow.setFlowEntryMatch(null);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700928
929 // start from desired flowId
Umesh Krishnaswamy2a82c642013-03-29 08:27:17 -0700930 //if (flow.flowId().value() < flowId.value()) {
931 // continue;
932 //}
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700933
934 // Summarize by making null flow entry fields that are not relevant to report
935 for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
936 flowEntry.setFlowEntryActions(null);
937 flowEntry.setFlowEntryMatch(null);
938 }
939
940 flowPaths.add(flow);
941 if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
942 break;
943 }
944 }
945
946 if (flowPaths.isEmpty()) {
947 log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700948 } else {
949 log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
950 }
951
952 return flowPaths;
953 }
954
955 /**
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800956 * Get all installed flows by all installers.
957 *
958 * @return the Flow Paths if found, otherwise null.
959 */
960 @Override
961 public ArrayList<FlowPath> getAllFlows() {
962 Iterable<IFlowPath> flowPathsObj = null;
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700963 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800964
965 try {
966 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
967 log.debug("Get all FlowPaths: found FlowPaths");
968 } else {
969 log.debug("Get all FlowPaths: no FlowPaths found");
970 }
971 } catch (Exception e) {
972 // TODO: handle exceptions
973 conn.endTx(Transaction.ROLLBACK);
974 log.error(":getAllFlowPaths failed");
975 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700976 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
977 conn.endTx(Transaction.COMMIT);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700978 return flowPaths; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700979 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800980
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800981 for (IFlowPath flowObj : flowPathsObj) {
982 //
983 // Extract the Flow state
984 //
985 FlowPath flowPath = extractFlowPath(flowObj);
Pavlin Radoslavov3f2af732013-03-29 15:29:35 -0700986 if (flowPath != null)
987 flowPaths.add(flowPath);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800988 }
989
990 conn.endTx(Transaction.COMMIT);
991
992 return flowPaths;
993 }
994
995 /**
996 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
997 *
998 * @param flowObj the object to extract the Flow Path State from.
999 * @return the extracted Flow Path State.
1000 */
1001 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001002 //
1003 // Extract the Flow state
1004 //
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001005 String flowIdStr = flowObj.getFlowId();
1006 String installerIdStr = flowObj.getInstallerId();
1007 String srcSwitchStr = flowObj.getSrcSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001008 Short srcPortShort = flowObj.getSrcPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001009 String dstSwitchStr = flowObj.getDstSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001010 Short dstPortShort = flowObj.getDstPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001011
1012 if ((flowIdStr == null) ||
1013 (installerIdStr == null) ||
1014 (srcSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001015 (srcPortShort == null) ||
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001016 (dstSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001017 (dstPortShort == null)) {
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001018 // TODO: A work-around, becauuse of some bogus database objects
1019 return null;
1020 }
1021
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001022 FlowPath flowPath = new FlowPath();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001023 flowPath.setFlowId(new FlowId(flowIdStr));
1024 flowPath.setInstallerId(new CallerId(installerIdStr));
1025 flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001026 flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001027 flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001028 flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001029 //
1030 // Extract the match conditions common for all Flow Entries
1031 //
1032 {
1033 FlowEntryMatch match = new FlowEntryMatch();
1034 Short matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1035 if (matchEthernetFrameType != null)
1036 match.enableEthernetFrameType(matchEthernetFrameType);
1037 String matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1038 if (matchSrcIPv4Net != null)
1039 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1040 String matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1041 if (matchDstIPv4Net != null)
1042 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1043 String matchSrcMac = flowObj.getMatchSrcMac();
1044 if (matchSrcMac != null)
1045 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1046 String matchDstMac = flowObj.getMatchDstMac();
1047 if (matchDstMac != null)
1048 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1049 flowPath.setFlowEntryMatch(match);
1050 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001051
1052 //
1053 // Extract all Flow Entries
1054 //
1055 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1056 for (IFlowEntry flowEntryObj : flowEntries) {
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001057 FlowEntry flowEntry = extractFlowEntry(flowEntryObj);
1058 if (flowEntry == null)
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001059 continue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001060 flowPath.dataPath().flowEntries().add(flowEntry);
1061 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001062
1063 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001064 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001065
1066 /**
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001067 * Extract Flow Entry State from a Titan Database Object @ref IFlowEntry.
1068 *
1069 * @param flowEntryObj the object to extract the Flow Entry State from.
1070 * @return the extracted Flow Entry State.
1071 */
1072 private FlowEntry extractFlowEntry(IFlowEntry flowEntryObj) {
1073 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1074 String switchDpidStr = flowEntryObj.getSwitchDpid();
1075 String userState = flowEntryObj.getUserState();
1076 String switchState = flowEntryObj.getSwitchState();
1077
1078 if ((flowEntryIdStr == null) ||
1079 (switchDpidStr == null) ||
1080 (userState == null) ||
1081 (switchState == null)) {
1082 // TODO: A work-around, becauuse of some bogus database objects
1083 return null;
1084 }
1085
1086 FlowEntry flowEntry = new FlowEntry();
1087 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
1088 flowEntry.setDpid(new Dpid(switchDpidStr));
1089
1090 //
1091 // Extract the match conditions
1092 //
1093 FlowEntryMatch match = new FlowEntryMatch();
1094 Short matchInPort = flowEntryObj.getMatchInPort();
1095 if (matchInPort != null)
1096 match.enableInPort(new Port(matchInPort));
1097 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1098 if (matchEthernetFrameType != null)
1099 match.enableEthernetFrameType(matchEthernetFrameType);
1100 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1101 if (matchSrcIPv4Net != null)
1102 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1103 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1104 if (matchDstIPv4Net != null)
1105 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1106 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1107 if (matchSrcMac != null)
1108 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1109 String matchDstMac = flowEntryObj.getMatchDstMac();
1110 if (matchDstMac != null)
1111 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1112 flowEntry.setFlowEntryMatch(match);
1113
1114 //
1115 // Extract the actions
1116 //
1117 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
1118 Short actionOutputPort = flowEntryObj.getActionOutput();
1119 if (actionOutputPort != null) {
1120 FlowEntryAction action = new FlowEntryAction();
1121 action.setActionOutput(new Port(actionOutputPort));
1122 actions.add(action);
1123 }
1124 flowEntry.setFlowEntryActions(actions);
1125 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
1126 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
1127 //
1128 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
1129 // and FlowEntryErrorState.
1130 //
1131 return flowEntry;
1132 }
1133
1134 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001135 * Add and maintain a shortest-path flow.
1136 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001137 * NOTE: The Flow Path argument does NOT contain flow entries.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001138 *
1139 * @param flowPath the Flow Path with the endpoints and the match
1140 * conditions to install.
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001141 * @return the added shortest-path flow on success, otherwise null.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001142 */
1143 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001144 public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001145 String dataPathSummaryStr = null;
1146
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001147 //
1148 // Do the shortest path computation
1149 //
1150 DataPath dataPath =
1151 topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
1152 flowPath.dataPath().dstPort());
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001153 if (dataPath == null) {
1154 // We need the DataPath to populate the Network MAP
1155 dataPath = new DataPath();
1156 dataPath.setSrcPort(flowPath.dataPath().srcPort());
1157 dataPath.setDstPort(flowPath.dataPath().dstPort());
1158 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001159
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001160 // Compute the Data Path summary
1161 dataPathSummaryStr = dataPath.dataPathSummary();
1162
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001163 //
1164 // Set the incoming port matching and the outgoing port output
1165 // actions for each flow entry.
1166 //
1167 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1168 // Set the incoming port matching
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001169 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001170 flowEntry.setFlowEntryMatch(flowEntryMatch);
1171 flowEntryMatch.enableInPort(flowEntry.inPort());
1172
1173 // Set the outgoing port output action
1174 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1175 if (flowEntryActions == null) {
1176 flowEntryActions = new ArrayList<FlowEntryAction>();
1177 flowEntry.setFlowEntryActions(flowEntryActions);
1178 }
1179 FlowEntryAction flowEntryAction = new FlowEntryAction();
1180 flowEntryAction.setActionOutput(flowEntry.outPort());
1181 flowEntryActions.add(flowEntryAction);
1182 }
1183
1184 //
1185 // Prepare the computed Flow Path
1186 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001187 FlowPath computedFlowPath = new FlowPath();
1188 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1189 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1190 computedFlowPath.setDataPath(dataPath);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001191 computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001192
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001193 FlowId flowId = new FlowId();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001194 if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001195 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001196
1197 // TODO: Mark the flow for maintenance purpose
1198
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001199 return (computedFlowPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001200 }
1201
1202 /**
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001203 * Reconcile all flows in a set.
1204 *
1205 * @param flowObjSet the set of flows that need to be reconciliated.
1206 */
1207 public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
1208 if (! flowObjSet.iterator().hasNext())
1209 return;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001210
1211 //
1212 // Remove the old Flow Entries, and add the new Flow Entries
1213 //
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001214
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001215 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001216 LinkedList<FlowPath> flowPaths = new LinkedList<FlowPath>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001217 for (IFlowPath flowObj : flowObjSet) {
1218 FlowPath flowPath = extractFlowPath(flowObj);
1219 if (flowPath == null)
1220 continue;
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001221 flowPaths.add(flowPath);
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001222
1223 //
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001224 // Remove the Flow Entries from the Network MAP
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001225 //
1226 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001227 LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001228 for (IFlowEntry flowEntryObj : flowEntries) {
1229 String dpidStr = flowEntryObj.getSwitchDpid();
1230 if (dpidStr == null)
1231 continue;
1232 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001233 /*
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001234 IOFSwitch mySwitch = mySwitches.get(dpid.value());
1235 if (mySwitch == null)
1236 continue; // Ignore the entry: not my switch
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001237 */
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001238 deleteFlowEntries.add(flowEntryObj);
1239 }
1240 for (IFlowEntry flowEntryObj : deleteFlowEntries) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001241 flowObj.removeFlowEntry(flowEntryObj);
1242 conn.utils().removeFlowEntry(conn, flowEntryObj);
1243 }
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001244 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001245
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001246 for (FlowPath flowPath : flowPaths) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001247 //
1248 // Delete the flow entries from the switches
1249 //
1250 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
1251 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001252 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1253 if (mySwitch == null) {
1254 // Not my switch
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001255 installRemoteFlowEntry(flowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001256 } else {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001257 installFlowEntry(mySwitch, flowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001258 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001259 }
1260
1261 //
1262 // Calculate the new shortest path and install it in the
1263 // Network MAP.
1264 //
1265 FlowPath addedFlowPath = addAndMaintainShortestPathFlow(flowPath);
1266 if (addedFlowPath == null) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001267 log.error("Cannot add Shortest Path Flow from {} to {}: path not found?",
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001268 flowPath.dataPath().srcPort().toString(),
1269 flowPath.dataPath().dstPort().toString());
1270 continue;
1271 }
1272
1273 //
1274 // Add the flow entries to the switches
1275 //
1276 for (FlowEntry flowEntry : addedFlowPath.dataPath().flowEntries()) {
1277 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001278 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1279 if (mySwitch == null) {
1280 // Not my switch
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001281 installRemoteFlowEntry(addedFlowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001282 continue;
1283 }
1284
1285 IFlowEntry flowEntryObj =
1286 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId());
1287 if (flowEntryObj == null) {
1288 //
1289 // TODO: Remove the "new Object[] wrapper in the statement
1290 // below after the SLF4J logger is upgraded to
1291 // Version 1.7.5
1292 //
1293 log.error("Cannot add Flow Entry to switch {} for Path Flow from {} to {} : Flow Entry not in the Network MAP",
1294 new Object[] {
1295 flowEntry.dpid(),
1296 flowPath.dataPath().srcPort(),
1297 flowPath.dataPath().dstPort()
1298 });
1299 continue;
1300 }
1301 // Update the Flow Entry Switch State in the Network MAP
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001302 if (installFlowEntry(mySwitch, addedFlowPath, flowEntry)) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001303 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1304 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001305 }
1306 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001307 }
1308
1309 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001310 * Install a Flow Entry on a switch.
1311 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001312 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001313 * @param flowObj the flow path object for the flow entry to install.
1314 * @param flowEntryObj the flow entry object to install.
1315 * @return true on success, otherwise false.
1316 */
1317 public boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
1318 IFlowEntry flowEntryObj) {
1319 FlowEntryId flowEntryId =
1320 new FlowEntryId(flowEntryObj.getFlowEntryId());
1321 String userState = flowEntryObj.getUserState();
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001322
1323 //
1324 // Create the Open Flow Flow Modification Entry to push
1325 //
1326 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1327 .getMessage(OFType.FLOW_MOD);
1328 long cookie = flowEntryId.value();
1329
1330 short flowModCommand = OFFlowMod.OFPFC_ADD;
1331 if (userState.equals("FE_USER_ADD")) {
1332 flowModCommand = OFFlowMod.OFPFC_ADD;
1333 } else if (userState.equals("FE_USER_MODIFY")) {
1334 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1335 } else if (userState.equals("FE_USER_DELETE")) {
1336 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1337 } else {
1338 // Unknown user state. Ignore the entry
1339 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1340 flowEntryId.toString(), userState);
1341 return false;
1342 }
1343
1344 //
1345 // Fetch the match conditions.
1346 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001347 // NOTE: The Flow matching conditions common for all Flow Entries are
1348 // used ONLY if a Flow Entry does NOT have the corresponding matching
1349 // condition set.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001350 //
1351 OFMatch match = new OFMatch();
1352 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001353
1354 // Match the Incoming Port
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001355 Short matchInPort = flowEntryObj.getMatchInPort();
1356 if (matchInPort != null) {
1357 match.setInputPort(matchInPort);
1358 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1359 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001360
1361 // Match the Ethernet Frame Type
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001362 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1363 if (matchEthernetFrameType == null)
1364 matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1365 if (matchEthernetFrameType != null) {
1366 match.setDataLayerType(matchEthernetFrameType);
1367 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1368 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001369
1370 // Match the Source IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001371 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1372 if (matchSrcIPv4Net == null)
1373 matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1374 if (matchSrcIPv4Net != null) {
1375 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
1376 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001377
1378 // Natch the Destination IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001379 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1380 if (matchDstIPv4Net == null)
1381 matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1382 if (matchDstIPv4Net != null) {
1383 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
1384 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001385
1386 // Match the Source MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001387 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1388 if (matchSrcMac == null)
1389 matchSrcMac = flowObj.getMatchSrcMac();
1390 if (matchSrcMac != null) {
1391 match.setDataLayerSource(matchSrcMac);
1392 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1393 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001394
1395 // Match the Destination MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001396 String matchDstMac = flowEntryObj.getMatchDstMac();
1397 if (matchDstMac == null)
1398 matchDstMac = flowObj.getMatchDstMac();
1399 if (matchDstMac != null) {
1400 match.setDataLayerDestination(matchDstMac);
1401 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1402 }
1403
1404 //
1405 // Fetch the actions
1406 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001407 // TODO: For now we support only the "OUTPUT" actions.
1408 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001409 List<OFAction> actions = new ArrayList<OFAction>();
1410 Short actionOutputPort = flowEntryObj.getActionOutput();
1411 if (actionOutputPort != null) {
1412 OFActionOutput action = new OFActionOutput();
1413 // XXX: The max length is hard-coded for now
1414 action.setMaxLength((short)0xffff);
1415 action.setPort(actionOutputPort);
1416 actions.add(action);
1417 }
1418
1419 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1420 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1421 .setPriority(PRIORITY_DEFAULT)
1422 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1423 .setCookie(cookie)
1424 .setCommand(flowModCommand)
1425 .setMatch(match)
1426 .setActions(actions)
1427 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1428 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1429 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1430 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1431 if (actionOutputPort != null)
1432 fm.setOutPort(actionOutputPort);
1433 }
1434
1435 //
1436 // TODO: Set the following flag
1437 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1438 // See method ForwardingBase::pushRoute()
1439 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001440
1441 //
1442 // Write the message to the switch
1443 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001444 try {
1445 messageDamper.write(mySwitch, fm, null);
1446 mySwitch.flush();
1447 //
1448 // TODO: We should use the OpenFlow Barrier mechanism
1449 // to check for errors, and update the SwitchState
1450 // for a flow entry after the Barrier message is
1451 // is received.
1452 //
1453 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1454 } catch (IOException e) {
1455 log.error("Failure writing flow mod from network map", e);
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001456 return false;
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001457 }
1458
1459 return true;
1460 }
1461
1462 /**
1463 * Install a Flow Entry on a switch.
1464 *
1465 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001466 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001467 * @param flowEntry the flow entry to install.
1468 * @return true on success, otherwise false.
1469 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001470 public boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1471 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001472 //
1473 // Create the OpenFlow Flow Modification Entry to push
1474 //
1475 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1476 .getMessage(OFType.FLOW_MOD);
1477 long cookie = flowEntry.flowEntryId().value();
1478
1479 short flowModCommand = OFFlowMod.OFPFC_ADD;
1480 if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
1481 flowModCommand = OFFlowMod.OFPFC_ADD;
1482 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
1483 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1484 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
1485 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1486 } else {
1487 // Unknown user state. Ignore the entry
1488 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1489 flowEntry.flowEntryId().toString(),
1490 flowEntry.flowEntryUserState());
1491 return false;
1492 }
1493
1494 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001495 // Fetch the match conditions.
1496 //
1497 // NOTE: The Flow matching conditions common for all Flow Entries are
1498 // used ONLY if a Flow Entry does NOT have the corresponding matching
1499 // condition set.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001500 //
1501 OFMatch match = new OFMatch();
1502 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001503 FlowEntryMatch flowPathMatch = flowPath.flowEntryMatch();
1504 FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
1505
1506 // Match the Incoming Port
1507 Port matchInPort = flowEntryMatch.inPort();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001508 if (matchInPort != null) {
1509 match.setInputPort(matchInPort.value());
1510 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1511 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001512
1513 // Match the Ethernet Frame Type
1514 Short matchEthernetFrameType = flowEntryMatch.ethernetFrameType();
1515 if ((matchEthernetFrameType == null) && (flowPathMatch != null)) {
1516 matchEthernetFrameType = flowPathMatch.ethernetFrameType();
1517 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001518 if (matchEthernetFrameType != null) {
1519 match.setDataLayerType(matchEthernetFrameType);
1520 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1521 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001522
1523 // Match the Source IPv4 Network prefix
1524 IPv4Net matchSrcIPv4Net = flowEntryMatch.srcIPv4Net();
1525 if ((matchSrcIPv4Net == null) && (flowPathMatch != null)) {
1526 matchSrcIPv4Net = flowPathMatch.srcIPv4Net();
1527 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001528 if (matchSrcIPv4Net != null) {
1529 match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
1530 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001531
1532 // Natch the Destination IPv4 Network prefix
1533 IPv4Net matchDstIPv4Net = flowEntryMatch.dstIPv4Net();
1534 if ((matchDstIPv4Net == null) && (flowPathMatch != null)) {
1535 matchDstIPv4Net = flowPathMatch.dstIPv4Net();
1536 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001537 if (matchDstIPv4Net != null) {
1538 match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
1539 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001540
1541 // Match the Source MAC address
1542 MACAddress matchSrcMac = flowEntryMatch.srcMac();
1543 if ((matchSrcMac == null) && (flowPathMatch != null)) {
1544 matchSrcMac = flowPathMatch.srcMac();
1545 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001546 if (matchSrcMac != null) {
1547 match.setDataLayerSource(matchSrcMac.toString());
1548 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1549 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001550
1551 // Match the Destination MAC address
1552 MACAddress matchDstMac = flowEntryMatch.dstMac();
1553 if ((matchDstMac == null) && (flowPathMatch != null)) {
1554 matchDstMac = flowPathMatch.dstMac();
1555 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001556 if (matchDstMac != null) {
1557 match.setDataLayerDestination(matchDstMac.toString());
1558 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1559 }
1560
1561 //
1562 // Fetch the actions
1563 //
1564 // TODO: For now we support only the "OUTPUT" actions.
1565 //
1566 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1567 List<OFAction> actions = new ArrayList<OFAction>();
1568 ArrayList<FlowEntryAction> flowEntryActions =
1569 flowEntry.flowEntryActions();
1570 for (FlowEntryAction flowEntryAction : flowEntryActions) {
1571 FlowEntryAction.ActionOutput actionOutput =
1572 flowEntryAction.actionOutput();
1573 if (actionOutput != null) {
1574 short actionOutputPort = actionOutput.port().value();
1575 OFActionOutput action = new OFActionOutput();
1576 // XXX: The max length is hard-coded for now
1577 action.setMaxLength((short)0xffff);
1578 action.setPort(actionOutputPort);
1579 actions.add(action);
1580 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1581 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1582 fm.setOutPort(actionOutputPort);
1583 }
1584 }
1585 }
1586
1587 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1588 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1589 .setPriority(PRIORITY_DEFAULT)
1590 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1591 .setCookie(cookie)
1592 .setCommand(flowModCommand)
1593 .setMatch(match)
1594 .setActions(actions)
1595 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1596
1597 //
1598 // TODO: Set the following flag
1599 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1600 // See method ForwardingBase::pushRoute()
1601 //
1602
1603 //
1604 // Write the message to the switch
1605 //
1606 try {
1607 messageDamper.write(mySwitch, fm, null);
1608 mySwitch.flush();
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001609 //
1610 // TODO: We should use the OpenFlow Barrier mechanism
1611 // to check for errors, and update the SwitchState
1612 // for a flow entry after the Barrier message is
1613 // is received.
1614 //
1615 // TODO: The FlowEntry Object in Titan should be set
1616 // to FE_SWITCH_UPDATED.
1617 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001618 } catch (IOException e) {
1619 log.error("Failure writing flow mod from network map", e);
1620 return false;
1621 }
1622 return true;
1623 }
1624
1625 /**
1626 * Remove a Flow Entry from a switch.
1627 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001628 * @param mySwitch the switch to remove the Flow Entry from.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001629 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001630 * @param flowEntry the flow entry to remove.
1631 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001632 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001633 public boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1634 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001635 //
1636 // The installFlowEntry() method implements both installation
1637 // and removal of flow entries.
1638 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001639 return (installFlowEntry(mySwitch, flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001640 }
1641
1642 /**
1643 * Install a Flow Entry on a remote controller.
1644 *
1645 * TODO: We need it now: Jono
1646 * - For now it will make a REST call to the remote controller.
1647 * - Internally, it needs to know the name of the remote controller.
1648 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001649 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001650 * @param flowEntry the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001651 * @return true on success, otherwise false.
1652 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001653 public boolean installRemoteFlowEntry(FlowPath flowPath,
1654 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001655 // TODO: We need it now: Jono
1656 // - For now it will make a REST call to the remote controller.
1657 // - Internally, it needs to know the name of the remote controller.
1658 return true;
1659 }
1660
1661 /**
1662 * Remove a flow entry on a remote controller.
1663 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001664 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001665 * @param flowEntry the flow entry to remove.
1666 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001667 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001668 public boolean removeRemoteFlowEntry(FlowPath flowPath,
1669 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001670 //
1671 // The installRemoteFlowEntry() method implements both installation
1672 // and removal of flow entries.
1673 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001674 return (installRemoteFlowEntry(flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001675 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001676}