blob: 6d3e4554b758549791dfecd7eeea2efac6e50186 [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 Radoslavovb6f53542013-03-01 16:02:14 -08006import java.util.EnumSet;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08007import java.util.HashMap;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07008import java.util.HashSet;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +00009import java.util.LinkedList;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080010import java.util.List;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080011import java.util.Map;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070012import java.util.TreeMap;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070013import java.util.Collections;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070014import java.util.concurrent.BlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080015import java.util.concurrent.Executors;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070016import java.util.concurrent.LinkedBlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080017import java.util.concurrent.ScheduledExecutorService;
18import java.util.concurrent.ScheduledFuture;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070019import java.util.concurrent.ThreadPoolExecutor;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080020import java.util.concurrent.TimeUnit;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080021
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080022import net.floodlightcontroller.core.IFloodlightProviderService;
23import net.floodlightcontroller.core.INetMapStorage;
24import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
25import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070026import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
Pankaj Berded0079742013-03-27 17:53:25 -070027import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070028import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080029import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080030import net.floodlightcontroller.core.module.FloodlightModuleContext;
31import net.floodlightcontroller.core.module.FloodlightModuleException;
32import net.floodlightcontroller.core.module.IFloodlightModule;
33import net.floodlightcontroller.core.module.IFloodlightService;
34import net.floodlightcontroller.flowcache.IFlowService;
35import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
36import net.floodlightcontroller.restserver.IRestApiService;
37import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080038import net.floodlightcontroller.util.DataPath;
39import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080040import net.floodlightcontroller.util.DataPathEndpoints;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080041import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070042import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080043import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070044import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080045import net.floodlightcontroller.util.FlowEntrySwitchState;
46import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080047import net.floodlightcontroller.util.FlowId;
48import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070049import net.floodlightcontroller.util.IPv4Net;
50import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080051import net.floodlightcontroller.util.OFMessageDamper;
52import net.floodlightcontroller.util.Port;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070053import net.floodlightcontroller.util.SwitchPort;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070054import net.onrc.onos.flow.IFlowManager;
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 Radoslavovb9fe6b42013-03-27 16:25:05 -070069public class FlowManager implements IFloodlightModule, IFlowService, IFlowManager, 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 Radoslavov01391c92013-03-14 17:13:21 -070090 private static long nextFlowEntryId = 1;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070091 private static long measurementFlowId = 100000;
92 private static String measurementFlowIdStr = "0x186a0"; // 100000
93 private long modifiedMeasurementFlowTime = 0;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070094
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080095 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080096 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
97
98 // The periodic task(s)
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070099 private final ScheduledExecutorService measureShortestPathScheduler =
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800100 Executors.newScheduledThreadPool(1);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700101 private final ScheduledExecutorService measureMapReaderScheduler =
102 Executors.newScheduledThreadPool(1);
103 private final ScheduledExecutorService mapReaderScheduler =
104 Executors.newScheduledThreadPool(1);
105
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700106 private BlockingQueue<Runnable> shortestPathQueue = new LinkedBlockingQueue<Runnable>();
107 private ThreadPoolExecutor shortestPathExecutor =
108 new ThreadPoolExecutor(10, 10, 5, TimeUnit.SECONDS, shortestPathQueue);
109
110 class ShortestPathTask implements Runnable {
111 private int hint;
112 private ITopoRouteService topoRouteService;
113 private ArrayList<DataPath> dpList;
114
115 public ShortestPathTask(int hint,
116 ITopoRouteService topoRouteService,
117 ArrayList<DataPath> dpList) {
118 this.hint = hint;
119 this.topoRouteService = topoRouteService;
120 this.dpList = dpList;
121 }
122
123 @Override
124 public void run() {
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700125 /*
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700126 String logMsg = "MEASUREMENT: Running Thread hint " + this.hint;
127 log.debug(logMsg);
128 long startTime = System.nanoTime();
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700129 */
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700130 for (DataPath dp : this.dpList) {
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700131 topoRouteService.getTopoShortestPath(dp.srcPort(), dp.dstPort());
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700132 }
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700133 /*
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700134 long estimatedTime = System.nanoTime() - startTime;
135 double rate = (estimatedTime > 0)? ((double)dpList.size() * 1000000000) / estimatedTime: 0.0;
136 logMsg = "MEASUREMENT: Computed Thread hint " + hint + ": " + dpList.size() + " shortest paths in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " flows/s";
137 log.debug(logMsg);
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700138 */
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700139 }
140 }
141
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700142 final Runnable measureShortestPath = new Runnable() {
143 public void run() {
144 log.debug("Recomputing Shortest Paths from the Network Map Flows...");
145 if (floodlightProvider == null) {
146 log.debug("FloodlightProvider service not found!");
147 return;
148 }
149
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700150 if (topoRouteService == null) {
151 log.debug("Topology Route Service not found");
152 return;
153 }
154
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700155 int leftoverQueueSize = shortestPathExecutor.getQueue().size();
156 if (leftoverQueueSize > 0) {
157 String logMsg = "MEASUREMENT: Leftover Shortest Path Queue Size: " + leftoverQueueSize;
158 log.debug(logMsg);
159 return;
160 }
161 log.debug("MEASUREMENT: Beginning Shortest Path Computation");
162
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700163 //
164 // Recompute the Shortest Paths for all Flows
165 //
166 int counter = 0;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700167 int hint = 0;
168 ArrayList<DataPath> dpList = new ArrayList<DataPath>();
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700169 long startTime = System.nanoTime();
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700170
171 topoRouteService.prepareShortestPathTopo();
172
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700173 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
174 for (IFlowPath flowPathObj : allFlowPaths) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700175 FlowId flowId = new FlowId(flowPathObj.getFlowId());
176
177 // log.debug("Found Path {}", flowId.toString());
178 Dpid srcDpid = new Dpid(flowPathObj.getSrcSwitch());
179 Port srcPort = new Port(flowPathObj.getSrcPort());
180 Dpid dstDpid = new Dpid(flowPathObj.getDstSwitch());
181 Port dstPort = new Port(flowPathObj.getDstPort());
182 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
183 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700184
185 /*
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700186 DataPath dp = new DataPath();
187 dp.setSrcPort(srcSwitchPort);
188 dp.setDstPort(dstSwitchPort);
189 dpList.add(dp);
190 if ((dpList.size() % 10) == 0) {
191 shortestPathExecutor.execute(
192 new ShortestPathTask(hint, topoRouteService,
193 dpList));
194 dpList = new ArrayList<DataPath>();
195 hint++;
196 }
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700197 */
198
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700199 DataPath dataPath =
200 topoRouteService.getTopoShortestPath(srcSwitchPort,
201 dstSwitchPort);
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700202 counter++;
203 }
204 if (dpList.size() > 0) {
205 shortestPathExecutor.execute(
206 new ShortestPathTask(hint, topoRouteService,
207 dpList));
208 }
209
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700210 /*
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700211 // Wait for all tasks to finish
212 try {
213 while (shortestPathExecutor.getQueue().size() > 0) {
214 Thread.sleep(100);
215 }
216 } catch (InterruptedException ex) {
217 log.debug("MEASUREMENT: Shortest Path Computation interrupted");
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700218 }
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700219 */
220
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700221 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700222 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700223
224 long estimatedTime = System.nanoTime() - startTime;
225 double rate = (estimatedTime > 0)? ((double)counter * 1000000000) / estimatedTime: 0.0;
226 String logMsg = "MEASUREMENT: Computed " + counter + " shortest paths in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " flows/s";
227 log.debug(logMsg);
228 }
229 };
230
231 final Runnable measureMapReader = new Runnable() {
232 public void run() {
233 if (floodlightProvider == null) {
234 log.debug("FloodlightProvider service not found!");
235 return;
236 }
237
238 //
239 // Fetch all Flow Entries
240 //
241 int counter = 0;
242 long startTime = System.nanoTime();
243 Iterable<IFlowEntry> allFlowEntries = conn.utils().getAllFlowEntries(conn);
244 for (IFlowEntry flowEntryObj : allFlowEntries) {
245 counter++;
246 FlowEntryId flowEntryId =
247 new FlowEntryId(flowEntryObj.getFlowEntryId());
248 String userState = flowEntryObj.getUserState();
249 String switchState = flowEntryObj.getSwitchState();
250 }
251 conn.endTx(Transaction.COMMIT);
252
253 long estimatedTime = System.nanoTime() - startTime;
254 double rate = (estimatedTime > 0)? ((double)counter * 1000000000) / estimatedTime: 0.0;
255 String logMsg = "MEASUREMENT: Fetched " + counter + " flow entries in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " entries/s";
256 log.debug(logMsg);
257 }
258 };
259
260 final Runnable mapReader = new Runnable() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800261 public void run() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800262 if (floodlightProvider == null) {
263 log.debug("FloodlightProvider service not found!");
264 return;
265 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000266 Map<Long, IOFSwitch> mySwitches =
267 floodlightProvider.getSwitches();
268 Map<Long, IFlowEntry> myFlowEntries =
269 new TreeMap<Long, IFlowEntry>();
270 LinkedList<IFlowEntry> deleteFlowEntries =
271 new LinkedList<IFlowEntry>();
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700272
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700273
274 //
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700275 // Fetch all Flow Entries and select only my Flow Entries
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000276 // that need to be undated into the switches.
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700277 //
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000278 Iterable<IFlowEntry> allFlowEntries =
279 conn.utils().getAllFlowEntries(conn);
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700280 for (IFlowEntry flowEntryObj : allFlowEntries) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000281 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700282 String userState = flowEntryObj.getUserState();
283 String switchState = flowEntryObj.getSwitchState();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000284 String dpidStr = flowEntryObj.getSwitchDpid();
285 if ((flowEntryIdStr == null) ||
286 (userState == null) ||
287 (switchState == null) ||
288 (dpidStr == null)) {
289 log.debug("IGNORING Flow Entry entry with null fields");
290 continue;
291 }
292 FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
293 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800294
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000295 /*
296 log.debug("Found Flow Entry Id = {} {}",
Pavlin Radoslavov79a67c12013-03-15 21:05:53 -0700297 flowEntryId.toString(),
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000298 "DPID = " + dpid.toString() +
299 " User State: " + userState +
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700300 " Switch State: " + switchState);
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000301 */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800302
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000303 if (! switchState.equals("FE_SWITCH_NOT_UPDATED"))
304 continue; // Ignore the entry: nothing to do
305
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800306 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000307 if (mySwitch == null)
308 continue; // Ignore the entry: not my switch
309
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700310 myFlowEntries.put(flowEntryId.value(), flowEntryObj);
311 }
312
313 //
314 // Process my Flow Entries
315 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700316 boolean processed_measurement_flow = false;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700317 for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
318 IFlowEntry flowEntryObj = entry.getValue();
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700319 // Code for measurement purpose
320 {
321 IFlowPath flowObj =
322 conn.utils().getFlowPathByFlowEntry(conn,
323 flowEntryObj);
324 if ((flowObj != null) &&
325 flowObj.getFlowId().equals(measurementFlowIdStr)) {
326 processed_measurement_flow = true;
327 }
328 }
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700329
330 //
331 // TODO: Eliminate the re-fetching of flowEntryId,
332 // userState, switchState, and dpid from the flowEntryObj.
333 //
334 FlowEntryId flowEntryId =
335 new FlowEntryId(flowEntryObj.getFlowEntryId());
336 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
337 String userState = flowEntryObj.getUserState();
338 String switchState = flowEntryObj.getSwitchState();
339 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000340 if (mySwitch == null)
341 continue; // Shouldn't happen
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800342
343 //
344 // Create the Open Flow Flow Modification Entry to push
345 //
346 OFFlowMod fm =
347 (OFFlowMod) floodlightProvider.getOFMessageFactory()
348 .getMessage(OFType.FLOW_MOD);
349 long cookie = flowEntryId.value();
350
351 short flowModCommand = OFFlowMod.OFPFC_ADD;
352 if (userState.equals("FE_USER_ADD")) {
353 flowModCommand = OFFlowMod.OFPFC_ADD;
354 } else if (userState.equals("FE_USER_MODIFY")) {
355 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
356 } else if (userState.equals("FE_USER_DELETE")) {
357 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
358 } else {
359 // Unknown user state. Ignore the entry
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700360 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
361 flowEntryId.toString(), userState);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800362 continue;
363 }
364
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700365 //
366 // Fetch the match conditions
367 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800368 OFMatch match = new OFMatch();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700369 match.setWildcards(OFMatch.OFPFW_ALL);
370 Short matchInPort = flowEntryObj.getMatchInPort();
371 if (matchInPort != null) {
372 match.setInputPort(matchInPort);
373 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
374 }
375 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
376 if (matchEthernetFrameType != null) {
377 match.setDataLayerType(matchEthernetFrameType);
378 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
379 }
380 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
381 if (matchSrcIPv4Net != null) {
382 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
383 }
384 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
385 if (matchDstIPv4Net != null) {
386 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
387 }
388 String matchSrcMac = flowEntryObj.getMatchSrcMac();
389 if (matchSrcMac != null) {
390 match.setDataLayerSource(matchSrcMac);
391 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
392 }
393 String matchDstMac = flowEntryObj.getMatchDstMac();
394 if (matchDstMac != null) {
395 match.setDataLayerDestination(matchDstMac);
396 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
397 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700398
399 //
400 // Fetch the actions
401 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800402 List<OFAction> actions = new ArrayList<OFAction>();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700403 Short actionOutputPort = flowEntryObj.getActionOutput();
404 if (actionOutputPort != null) {
405 OFActionOutput action = new OFActionOutput();
406 // XXX: The max length is hard-coded for now
407 action.setMaxLength((short)0xffff);
408 action.setPort(actionOutputPort);
409 actions.add(action);
410 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800411
412 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
413 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700414 .setPriority(PRIORITY_DEFAULT)
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800415 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
416 .setCookie(cookie)
417 .setCommand(flowModCommand)
418 .setMatch(match)
419 .setActions(actions)
420 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700421 fm.setOutPort(OFPort.OFPP_NONE.getValue());
422 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
423 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
424 if (actionOutputPort != null)
425 fm.setOutPort(actionOutputPort);
426 }
427
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800428 //
429 // TODO: Set the following flag
430 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
431 // See method ForwardingBase::pushRoute()
432 //
433 try {
434 messageDamper.write(mySwitch, fm, null);
435 mySwitch.flush();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000436 //
437 // TODO: We should use the OpenFlow Barrier mechanism
438 // to check for errors, and update the SwitchState
439 // for a flow entry after the Barrier message is
440 // is received.
441 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800442 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
443 if (userState.equals("FE_USER_DELETE")) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000444 // An entry that needs to be deleted.
445 deleteFlowEntries.add(flowEntryObj);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800446 }
447 } catch (IOException e) {
448 log.error("Failure writing flow mod from network map", e);
449 }
450 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000451
452 //
453 // Delete all entries marked for deletion
454 //
455 // TODO: We should use the OpenFlow Barrier mechanism
456 // to check for errors, and delete the Flow Entries after the
457 // Barrier message is received.
458 //
459 while (! deleteFlowEntries.isEmpty()) {
460 IFlowEntry flowEntryObj = deleteFlowEntries.poll();
461 IFlowPath flowObj =
462 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
463 if (flowObj == null) {
464 log.debug("Did not find FlowPath to be deleted");
465 continue;
466 }
467 flowObj.removeFlowEntry(flowEntryObj);
468 conn.utils().removeFlowEntry(conn, flowEntryObj);
469
470 // Test whether the last flow entry
471 Iterable<IFlowEntry> tmpflowEntries =
472 flowObj.getFlowEntries();
473 boolean found = false;
474 for (IFlowEntry tmpflowEntryObj : tmpflowEntries) {
475 found = true;
476 break;
477 }
478 if (! found) {
479 // Remove the Flow Path as well
480 conn.utils().removeFlowPath(conn, flowObj);
481 }
482 }
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700483
484
485 //
486 // Fetch and recompute the Shortest Path for those
487 // Flow Paths this controller is responsible for.
488 //
489
490 /*
491 * TODO: For now, the computation of the reconciliation is
492 * commented-out.
493 */
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700494 topoRouteService.prepareShortestPathTopo();
495 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
496 HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
497 for (IFlowPath flowPathObj : allFlowPaths) {
498 if (flowPathObj == null)
499 continue;
500 String dataPathSummaryStr = flowPathObj.getDataPathSummary();
501 if (dataPathSummaryStr == null)
502 continue; // Could be invalid entry?
503 if (dataPathSummaryStr.isEmpty())
504 continue; // No need to maintain this flow
505
506 // Fetch the fields needed to recompute the shortest path
507 String flowIdStr = flowPathObj.getFlowId();
508 String srcDpidStr = flowPathObj.getSrcSwitch();
509 Short srcPortShort = flowPathObj.getSrcPort();
510 String dstDpidStr = flowPathObj.getDstSwitch();
511 Short dstPortShort = flowPathObj.getDstPort();
512 if ((flowIdStr == null) ||
513 (srcDpidStr == null) ||
514 (srcPortShort == null) ||
515 (dstDpidStr == null) ||
516 (dstPortShort == null)) {
517 log.debug("IGNORING Flow Path entry with null fields");
518 continue;
519 }
520
521 FlowId flowId = new FlowId(flowIdStr);
522 Dpid srcDpid = new Dpid(srcDpidStr);
523 Port srcPort = new Port(srcPortShort);
524 Dpid dstDpid = new Dpid(dstDpidStr);
525 Port dstPort = new Port(dstPortShort);
526 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
527 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
Pavlin Radoslavov832aa652013-03-29 16:21:59 -0700528 //
529 // NOTE: Using here the regular getShortestPath() method
530 // won't work here, because that method calls internally
531 // "conn.endTx(Transaction.COMMIT)", and that will
532 // invalidate all handlers to the Titan database.
533 // If we want to experiment with calling here
534 // getShortestPath(), we need to refactor that code
535 // to avoid closing the transaction.
536 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700537 DataPath dataPath =
538 topoRouteService.getTopoShortestPath(srcSwitchPort,
539 dstSwitchPort);
540 String newDataPathSummaryStr = dataPath.dataPathSummary();
541 if (dataPathSummaryStr.equals(newDataPathSummaryStr))
542 continue; // Nothing changed
543
544 //
545 // Use the source DPID as a heuristic to decide
546 // which controller is responsible for maintaining the
547 // shortest path.
548 // NOTE: This heuristic is error-prone: if the switch
549 // goes away and no controller is responsible for that
550 // switch, then the original Flow Path is not cleaned-up
551 //
552 IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
553 if (mySwitch == null)
554 continue; // Ignore: not my responsibility
555
556 log.debug("RECONCILE: Need to Reconcile Shortest Path for FlowID {}",
557 flowId.toString());
558 flowObjSet.add(flowPathObj);
559 }
560 reconcileFlows(flowObjSet);
561 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700562
563
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800564 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700565
566 if (processed_measurement_flow) {
567 long estimatedTime = System.nanoTime() - modifiedMeasurementFlowTime;
568 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
569 (double)estimatedTime / 1000000000 + " sec";
570 log.debug(logMsg);
571 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800572 }
573 };
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700574
575 /*
576 final ScheduledFuture<?> measureShortestPathHandle =
577 measureShortestPathScheduler.scheduleAtFixedRate(measureShortestPath, 10, 10, TimeUnit.SECONDS);
578 */
579
Pavlin Radoslavove38319c2013-03-21 16:20:00 -0700580 /*
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700581 final ScheduledFuture<?> measureMapReaderHandle =
582 measureMapReaderScheduler.scheduleAtFixedRate(measureMapReader, 10, 10, TimeUnit.SECONDS);
Pavlin Radoslavove38319c2013-03-21 16:20:00 -0700583 */
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700584
585 final ScheduledFuture<?> mapReaderHandle =
586 mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800587
588 @Override
589 public void init(String conf) {
590 conn = GraphDBConnection.getInstance(conf);
591 }
592
593 public void finalize() {
594 close();
595 }
596
597 @Override
598 public void close() {
599 conn.close();
600 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800601
602 @Override
603 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
604 Collection<Class<? extends IFloodlightService>> l =
605 new ArrayList<Class<? extends IFloodlightService>>();
606 l.add(IFlowService.class);
607 return l;
608 }
609
610 @Override
611 public Map<Class<? extends IFloodlightService>, IFloodlightService>
612 getServiceImpls() {
613 Map<Class<? extends IFloodlightService>,
614 IFloodlightService> m =
615 new HashMap<Class<? extends IFloodlightService>,
616 IFloodlightService>();
617 m.put(IFlowService.class, this);
618 return m;
619 }
620
621 @Override
622 public Collection<Class<? extends IFloodlightService>>
623 getModuleDependencies() {
624 Collection<Class<? extends IFloodlightService>> l =
625 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800626 l.add(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700627 l.add(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800628 l.add(IRestApiService.class);
629 return l;
630 }
631
632 @Override
633 public void init(FloodlightModuleContext context)
634 throws FloodlightModuleException {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700635 this.context = context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800636 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700637 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800638 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800639 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
640 EnumSet.of(OFType.FLOW_MOD),
641 OFMESSAGE_DAMPER_TIMEOUT);
642 // TODO: An ugly hack!
643 String conf = "/tmp/cassandra.titan";
644 this.init(conf);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800645 }
646
647 @Override
648 public void startUp(FloodlightModuleContext context) {
649 restApi.addRestletRoutable(new FlowWebRoutable());
Pavlin Radoslavov80ca6302013-03-20 02:08:09 -0700650
651 //
652 // Extract all flow entries and assign the next Flow Entry ID
653 // to be larger than the largest Flow Entry ID
654 //
655 Iterable<IFlowEntry> allFlowEntries = conn.utils().getAllFlowEntries(conn);
656 for (IFlowEntry flowEntryObj : allFlowEntries) {
657 FlowEntryId flowEntryId =
658 new FlowEntryId(flowEntryObj.getFlowEntryId());
659 if (flowEntryId.value() >= nextFlowEntryId)
660 nextFlowEntryId = flowEntryId.value() + 1;
661 }
662 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800663 }
664
665 /**
666 * Add a flow.
667 *
668 * Internally, ONOS will automatically register the installer for
669 * receiving Flow Path Notifications for that path.
670 *
671 * @param flowPath the Flow Path to install.
672 * @param flowId the return-by-reference Flow ID as assigned internally.
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700673 * @param dataPathSummaryStr the data path summary string if the added
674 * flow will be maintained internally, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800675 * @return true on success, otherwise false.
676 */
677 @Override
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700678 public boolean addFlow(FlowPath flowPath, FlowId flowId,
679 String dataPathSummaryStr) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700680 if (flowPath.flowId().value() == measurementFlowId) {
681 modifiedMeasurementFlowTime = System.nanoTime();
682 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800683
684 //
685 // Assign the FlowEntry IDs
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700686 // Right now every new flow entry gets a new flow entry ID
687 // TODO: This needs to be redesigned!
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800688 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800689 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700690 long id = nextFlowEntryId++;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800691 flowEntry.setFlowEntryId(new FlowEntryId(id));
692 }
693
694 IFlowPath flowObj = null;
695 try {
696 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
697 != null) {
698 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
699 flowPath.flowId().toString());
700 } else {
701 flowObj = conn.utils().newFlowPath(conn);
702 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
703 flowPath.flowId().toString());
704 }
705 } catch (Exception e) {
706 // TODO: handle exceptions
707 conn.endTx(Transaction.ROLLBACK);
708 log.error(":addFlow FlowId:{} failed",
709 flowPath.flowId().toString());
710 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700711 if (flowObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000712 log.error(":addFlow FlowId:{} failed: Flow object not created",
713 flowPath.flowId().toString());
714 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800715 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700716 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800717
718 //
719 // Set the Flow key:
720 // - flowId
721 //
722 flowObj.setFlowId(flowPath.flowId().toString());
723 flowObj.setType("flow");
724
725 //
726 // Set the Flow attributes:
727 // - flowPath.installerId()
728 // - flowPath.dataPath().srcPort()
729 // - flowPath.dataPath().dstPort()
730 //
731 flowObj.setInstallerId(flowPath.installerId().toString());
732 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
733 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
734 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
735 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
736
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700737 if (dataPathSummaryStr != null) {
738 flowObj.setDataPathSummary(dataPathSummaryStr);
739 } else {
740 flowObj.setDataPathSummary("");
741 }
742
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800743 // Flow edges:
744 // HeadFE
745
746
747 //
748 // Flow Entries:
749 // flowPath.dataPath().flowEntries()
750 //
751 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
752 IFlowEntry flowEntryObj = null;
753 boolean found = false;
754 try {
755 if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
756 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
757 flowEntry.flowEntryId().toString());
758 found = true;
759 } else {
760 flowEntryObj = conn.utils().newFlowEntry(conn);
761 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
762 flowEntry.flowEntryId().toString());
763 }
764 } catch (Exception e) {
765 // TODO: handle exceptions
766 conn.endTx(Transaction.ROLLBACK);
767 log.error(":addFlow FlowEntryId:{} failed",
768 flowEntry.flowEntryId().toString());
769 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700770 if (flowEntryObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000771 log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created",
772 flowEntry.flowEntryId().toString());
773 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800774 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700775 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800776
777 //
778 // Set the Flow Entry key:
779 // - flowEntry.flowEntryId()
780 //
781 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
782 flowEntryObj.setType("flow_entry");
783
784 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700785 // Set the Flow Entry Edges and attributes:
786 // - Switch edge
787 // - InPort edge
788 // - OutPort edge
789 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800790 // - flowEntry.flowEntryMatch()
791 // - flowEntry.flowEntryActions()
792 // - flowEntry.dpid()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800793 // - flowEntry.flowEntryUserState()
794 // - flowEntry.flowEntrySwitchState()
795 // - flowEntry.flowEntryErrorState()
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700796 // - flowEntry.matchInPort()
797 // - flowEntry.matchEthernetFrameType()
798 // - flowEntry.matchSrcIPv4Net()
799 // - flowEntry.matchDstIPv4Net()
800 // - flowEntry.matchSrcMac()
801 // - flowEntry.matchDstMac()
802 // - flowEntry.actionOutput()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800803 //
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700804 ISwitchObject sw =
805 conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800806 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
Pankaj Berded0079742013-03-27 17:53:25 -0700807 flowEntryObj.setSwitch(sw);
808 if (flowEntry.flowEntryMatch().matchInPort()) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700809 IPortObject inport =
810 conn.utils().searchPort(conn, flowEntry.dpid().toString(),
811 flowEntry.flowEntryMatch().inPort().value());
Pankaj Berded0079742013-03-27 17:53:25 -0700812 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
813 flowEntryObj.setInPort(inport);
814 }
815 if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
816 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
817 }
818 if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
819 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
820 }
821 if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
822 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
823 }
824 if (flowEntry.flowEntryMatch().matchSrcMac()) {
825 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
826 }
827 if (flowEntry.flowEntryMatch().matchDstMac()) {
828 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
829 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700830
831 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
Pankaj Berded0079742013-03-27 17:53:25 -0700832 if (fa.actionOutput() != null) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700833 IPortObject outport =
834 conn.utils().searchPort(conn,
835 flowEntry.dpid().toString(),
836 fa.actionOutput().port().value());
837 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
838 flowEntryObj.setOutPort(outport);
Pankaj Berded0079742013-03-27 17:53:25 -0700839 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700840 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800841 // TODO: Hacks with hard-coded state names!
842 if (found)
843 flowEntryObj.setUserState("FE_USER_MODIFY");
844 else
845 flowEntryObj.setUserState("FE_USER_ADD");
846 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
847 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700848 // TODO: Take care of the FlowEntryErrorState.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800849 //
850
851 // Flow Entries edges:
852 // Flow
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700853 // NextFE (TODO)
854 if (! found) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800855 flowObj.addFlowEntry(flowEntryObj);
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700856 flowEntryObj.setFlow(flowObj);
857 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800858 }
859 conn.endTx(Transaction.COMMIT);
860
861 //
862 // TODO: We need a proper Flow ID allocation mechanism.
863 //
864 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700865
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800866 return true;
867 }
868
869 /**
870 * Delete a previously added flow.
871 *
872 * @param flowId the Flow ID of the flow to delete.
873 * @return true on success, otherwise false.
874 */
875 @Override
876 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700877 if (flowId.value() == measurementFlowId) {
878 modifiedMeasurementFlowTime = System.nanoTime();
879 }
880
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800881 IFlowPath flowObj = null;
882 //
883 // We just mark the entries for deletion,
884 // and let the switches remove each individual entry after
885 // it has been removed from the switches.
886 //
887 try {
888 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
889 != null) {
890 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
891 flowId.toString());
892 } else {
893 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
894 flowId.toString());
895 }
896 } catch (Exception e) {
897 // TODO: handle exceptions
898 conn.endTx(Transaction.ROLLBACK);
899 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
900 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700901 if (flowObj == null) {
902 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800903 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700904 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800905
906 //
907 // Find and mark for deletion all Flow Entries
908 //
909 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
910 boolean empty = true; // TODO: an ugly hack
911 for (IFlowEntry flowEntryObj : flowEntries) {
912 empty = false;
913 // flowObj.removeFlowEntry(flowEntryObj);
914 // conn.utils().removeFlowEntry(conn, flowEntryObj);
915 flowEntryObj.setUserState("FE_USER_DELETE");
916 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
917 }
918 // Remove from the database empty flows
919 if (empty)
920 conn.utils().removeFlowPath(conn, flowObj);
921 conn.endTx(Transaction.COMMIT);
922
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800923 return true;
924 }
925
926 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700927 * Clear the state for a previously added flow.
928 *
929 * @param flowId the Flow ID of the flow to clear.
930 * @return true on success, otherwise false.
931 */
932 @Override
933 public boolean clearFlow(FlowId flowId) {
934 IFlowPath flowObj = null;
935 try {
936 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
937 != null) {
938 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
939 flowId.toString());
940 } else {
941 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
942 flowId.toString());
943 }
944 } catch (Exception e) {
945 // TODO: handle exceptions
946 conn.endTx(Transaction.ROLLBACK);
947 log.error(":clearFlow FlowId:{} failed", flowId.toString());
948 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700949 if (flowObj == null) {
950 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700951 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700952 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700953
954 //
955 // Remove all Flow Entries
956 //
957 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
958 for (IFlowEntry flowEntryObj : flowEntries) {
959 flowObj.removeFlowEntry(flowEntryObj);
960 conn.utils().removeFlowEntry(conn, flowEntryObj);
961 }
962 // Remove the Flow itself
963 conn.utils().removeFlowPath(conn, flowObj);
964 conn.endTx(Transaction.COMMIT);
965
966 return true;
967 }
968
969 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800970 * Get a previously added flow.
971 *
972 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800973 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800974 */
975 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800976 public FlowPath getFlow(FlowId flowId) {
977 IFlowPath flowObj = null;
978 try {
979 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
980 != null) {
981 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
982 flowId.toString());
983 } else {
984 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
985 flowId.toString());
986 }
987 } catch (Exception e) {
988 // TODO: handle exceptions
989 conn.endTx(Transaction.ROLLBACK);
990 log.error(":getFlow FlowId:{} failed", flowId.toString());
991 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700992 if (flowObj == null) {
993 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800994 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700995 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800996
997 //
998 // Extract the Flow state
999 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001000 FlowPath flowPath = extractFlowPath(flowObj);
1001 conn.endTx(Transaction.COMMIT);
1002
1003 return flowPath;
1004 }
1005
1006 /**
1007 * Get all previously added flows by a specific installer for a given
1008 * data path endpoints.
1009 *
1010 * @param installerId the Caller ID of the installer of the flow to get.
1011 * @param dataPathEndpoints the data path endpoints of the flow to get.
1012 * @return the Flow Paths if found, otherwise null.
1013 */
1014 @Override
1015 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
1016 DataPathEndpoints dataPathEndpoints) {
1017 //
1018 // TODO: The implementation below is not optimal:
1019 // We fetch all flows, and then return only the subset that match
1020 // the query conditions.
1021 // We should use the appropriate Titan/Gremlin query to filter-out
1022 // the flows as appropriate.
1023 //
1024 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001025 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001026
1027 if (allFlows == null) {
1028 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001029 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001030 }
1031
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001032 for (FlowPath flow : allFlows) {
1033 //
1034 // TODO: String-based comparison is sub-optimal.
1035 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -08001036 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001037 //
1038 if (! flow.installerId().toString().equals(installerId.toString()))
1039 continue;
1040 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
1041 continue;
1042 }
1043 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
1044 continue;
1045 }
1046 flowPaths.add(flow);
1047 }
1048
1049 if (flowPaths.isEmpty()) {
1050 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001051 } else {
1052 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
1053 }
1054
1055 return flowPaths;
1056 }
1057
1058 /**
1059 * Get all installed flows by all installers for given data path endpoints.
1060 *
1061 * @param dataPathEndpoints the data path endpoints of the flows to get.
1062 * @return the Flow Paths if found, otherwise null.
1063 */
1064 @Override
1065 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
1066 //
1067 // TODO: The implementation below is not optimal:
1068 // We fetch all flows, and then return only the subset that match
1069 // the query conditions.
1070 // We should use the appropriate Titan/Gremlin query to filter-out
1071 // the flows as appropriate.
1072 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001073 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1074 ArrayList<FlowPath> allFlows = getAllFlows();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001075
1076 if (allFlows == null) {
1077 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001078 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001079 }
1080
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001081 for (FlowPath flow : allFlows) {
1082 //
1083 // TODO: String-based comparison is sub-optimal.
1084 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -08001085 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001086 //
1087 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
1088 continue;
1089 }
1090 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
1091 continue;
1092 }
1093 flowPaths.add(flow);
1094 }
1095
1096 if (flowPaths.isEmpty()) {
1097 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001098 } else {
1099 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
1100 }
1101
1102 return flowPaths;
1103 }
1104
1105 /**
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001106 * Get summary of all installed flows by all installers in a given range
1107 *
1108 * @param flowId the data path endpoints of the flows to get.
1109 * @param maxFlows: the maximum number of flows to be returned
1110 * @return the Flow Paths if found, otherwise null.
1111 */
1112 @Override
1113 public ArrayList<FlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
1114 //
1115 // TODO: The implementation below is not optimal:
1116 // We fetch all flows, and then return only the subset that match
1117 // the query conditions.
1118 // We should use the appropriate Titan/Gremlin query to filter-out
1119 // the flows as appropriate.
1120 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001121 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1122
1123 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001124
1125 if (allFlows == null) {
1126 log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001127 return flowPaths;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001128 }
1129
Umesh Krishnaswamy244b4ae2013-03-29 12:05:15 -07001130 Collections.sort(allFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001131
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001132 for (FlowPath flow : allFlows) {
1133
1134 // start from desired flowId
Umesh Krishnaswamy2a82c642013-03-29 08:27:17 -07001135 //if (flow.flowId().value() < flowId.value()) {
1136 // continue;
1137 //}
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001138
1139 // Summarize by making null flow entry fields that are not relevant to report
1140 for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
1141 flowEntry.setFlowEntryActions(null);
1142 flowEntry.setFlowEntryMatch(null);
1143 }
1144
1145 flowPaths.add(flow);
1146 if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
1147 break;
1148 }
1149 }
1150
1151 if (flowPaths.isEmpty()) {
1152 log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001153 } else {
1154 log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
1155 }
1156
1157 return flowPaths;
1158 }
1159
1160 /**
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001161 * Get all installed flows by all installers.
1162 *
1163 * @return the Flow Paths if found, otherwise null.
1164 */
1165 @Override
1166 public ArrayList<FlowPath> getAllFlows() {
1167 Iterable<IFlowPath> flowPathsObj = null;
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001168 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001169
1170 try {
1171 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
1172 log.debug("Get all FlowPaths: found FlowPaths");
1173 } else {
1174 log.debug("Get all FlowPaths: no FlowPaths found");
1175 }
1176 } catch (Exception e) {
1177 // TODO: handle exceptions
1178 conn.endTx(Transaction.ROLLBACK);
1179 log.error(":getAllFlowPaths failed");
1180 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001181 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
1182 conn.endTx(Transaction.COMMIT);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001183 return flowPaths; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001184 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001185
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001186 for (IFlowPath flowObj : flowPathsObj) {
1187 //
1188 // Extract the Flow state
1189 //
1190 FlowPath flowPath = extractFlowPath(flowObj);
Pavlin Radoslavov3f2af732013-03-29 15:29:35 -07001191 if (flowPath != null)
1192 flowPaths.add(flowPath);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001193 }
1194
1195 conn.endTx(Transaction.COMMIT);
1196
1197 return flowPaths;
1198 }
1199
1200 /**
1201 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
1202 *
1203 * @param flowObj the object to extract the Flow Path State from.
1204 * @return the extracted Flow Path State.
1205 */
1206 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001207 FlowPath flowPath = new FlowPath();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001208
1209 //
1210 // Extract the Flow state
1211 //
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001212 String flowIdStr = flowObj.getFlowId();
1213 String installerIdStr = flowObj.getInstallerId();
1214 String srcSwitchStr = flowObj.getSrcSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001215 Short srcPortShort = flowObj.getSrcPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001216 String dstSwitchStr = flowObj.getDstSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001217 Short dstPortShort = flowObj.getDstPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001218
1219 if ((flowIdStr == null) ||
1220 (installerIdStr == null) ||
1221 (srcSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001222 (srcPortShort == null) ||
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001223 (dstSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001224 (dstPortShort == null)) {
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001225 // TODO: A work-around, becauuse of some bogus database objects
1226 return null;
1227 }
1228
1229 flowPath.setFlowId(new FlowId(flowIdStr));
1230 flowPath.setInstallerId(new CallerId(installerIdStr));
1231 flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001232 flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001233 flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001234 flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001235
1236 //
1237 // Extract all Flow Entries
1238 //
1239 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1240 for (IFlowEntry flowEntryObj : flowEntries) {
1241 FlowEntry flowEntry = new FlowEntry();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001242 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1243 String switchDpidStr = flowEntryObj.getSwitchDpid();
1244 String userState = flowEntryObj.getUserState();
1245 String switchState = flowEntryObj.getSwitchState();
1246
1247 if ((flowEntryIdStr == null) ||
1248 (switchDpidStr == null) ||
1249 (userState == null) ||
1250 (switchState == null)) {
1251 // TODO: A work-around, becauuse of some bogus database objects
1252 continue;
1253 }
1254 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
1255 flowEntry.setDpid(new Dpid(switchDpidStr));
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -07001256
1257 //
1258 // Extract the match conditions
1259 //
1260 FlowEntryMatch match = new FlowEntryMatch();
1261 Short matchInPort = flowEntryObj.getMatchInPort();
1262 if (matchInPort != null)
1263 match.enableInPort(new Port(matchInPort));
1264 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1265 if (matchEthernetFrameType != null)
1266 match.enableEthernetFrameType(matchEthernetFrameType);
1267 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1268 if (matchSrcIPv4Net != null)
1269 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1270 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1271 if (matchDstIPv4Net != null)
1272 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1273 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1274 if (matchSrcMac != null)
1275 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1276 String matchDstMac = flowEntryObj.getMatchDstMac();
1277 if (matchDstMac != null)
1278 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1279 flowEntry.setFlowEntryMatch(match);
1280
1281 //
1282 // Extract the actions
1283 //
1284 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
1285 Short actionOutputPort = flowEntryObj.getActionOutput();
1286 if (actionOutputPort != null) {
1287 FlowEntryAction action = new FlowEntryAction();
1288 action.setActionOutput(new Port(actionOutputPort));
1289 actions.add(action);
1290 }
1291 flowEntry.setFlowEntryActions(actions);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001292 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001293 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
1294 //
Pavlin Radoslavovede97582013-03-08 18:57:28 -08001295 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001296 // and FlowEntryErrorState.
1297 //
1298 flowPath.dataPath().flowEntries().add(flowEntry);
1299 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001300
1301 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001302 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001303
1304 /**
1305 * Add and maintain a shortest-path flow.
1306 *
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001307 * NOTE: The Flow Path argument does NOT contain all flow entries.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001308 * Instead, it contains a single dummy flow entry that is used to
1309 * store the matching condition(s).
1310 * That entry is replaced by the appropriate entries from the
1311 * internally performed shortest-path computation.
1312 *
1313 * @param flowPath the Flow Path with the endpoints and the match
1314 * conditions to install.
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001315 * @return the added shortest-path flow on success, otherwise null.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001316 */
1317 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001318 public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001319 String dataPathSummaryStr = null;
1320
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001321 //
1322 // Do the shortest path computation
1323 //
1324 DataPath dataPath =
1325 topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
1326 flowPath.dataPath().dstPort());
1327 if (dataPath == null)
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001328 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001329
1330 FlowEntryMatch userFlowEntryMatch = null;
1331 if (! flowPath.dataPath().flowEntries().isEmpty()) {
1332 userFlowEntryMatch = flowPath.dataPath().flowEntries().get(0).flowEntryMatch();
1333 }
1334
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001335 // Compute the Data Path summary
1336 dataPathSummaryStr = dataPath.dataPathSummary();
1337
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001338 //
1339 // Set the incoming port matching and the outgoing port output
1340 // actions for each flow entry.
1341 //
1342 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1343 // Set the incoming port matching
1344 FlowEntryMatch flowEntryMatch = null;
1345 if (userFlowEntryMatch != null)
1346 flowEntryMatch = new FlowEntryMatch(userFlowEntryMatch);
1347 else
1348 flowEntryMatch = new FlowEntryMatch();
1349 flowEntry.setFlowEntryMatch(flowEntryMatch);
1350 flowEntryMatch.enableInPort(flowEntry.inPort());
1351
1352 // Set the outgoing port output action
1353 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1354 if (flowEntryActions == null) {
1355 flowEntryActions = new ArrayList<FlowEntryAction>();
1356 flowEntry.setFlowEntryActions(flowEntryActions);
1357 }
1358 FlowEntryAction flowEntryAction = new FlowEntryAction();
1359 flowEntryAction.setActionOutput(flowEntry.outPort());
1360 flowEntryActions.add(flowEntryAction);
1361 }
1362
1363 //
1364 // Prepare the computed Flow Path
1365 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001366 FlowPath computedFlowPath = new FlowPath();
1367 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1368 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1369 computedFlowPath.setDataPath(dataPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001370
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001371 FlowId flowId = new FlowId();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001372 if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001373 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001374
1375 // TODO: Mark the flow for maintenance purpose
1376
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001377 return (computedFlowPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001378 }
1379
1380 /**
1381 * Create a Flow from port to port.
1382 *
1383 * TODO: We don't need it for now.
1384 *
1385 * @param src_port the source port.
1386 * @param dest_port the destination port.
1387 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001388 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001389 public void createFlow(IPortObject src_port, IPortObject dest_port) {
1390 // TODO: We don't need it for now.
1391 }
1392
1393 /**
1394 * Get all Flows matching a source and a destination port.
1395 *
1396 * TODO: Pankaj might be implementing it later.
1397 *
1398 * @param src_port the source port to match.
1399 * @param dest_port the destination port to match.
1400 * @return all flows matching the source and the destination port.
1401 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001402 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001403 public Iterable<FlowPath> getFlows(IPortObject src_port,
1404 IPortObject dest_port) {
1405 // TODO: Pankaj might be implementing it later.
1406 return null;
1407 }
1408
1409 /**
1410 * Get all Flows going out from a port.
1411 *
1412 * TODO: We need it now: Pankaj
1413 *
1414 * @param port the port to match.
1415 * @return the list of flows that are going out from the port.
1416 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001417 @Override
Pankaj Berde83d83382013-03-28 13:55:34 -07001418 public Iterable<FlowPath> getOutFlows(IPortObject port) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001419 // TODO: We need it now: Pankaj
1420 return null;
1421 }
1422
1423 /**
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001424 * Reconcile all flows on inactive switch port.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001425 *
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001426 * @param portObject the port that has become inactive.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001427 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001428 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001429 public void reconcileFlows(IPortObject portObject) {
1430 Iterable<IFlowEntry> inFlowEntries = portObject.getInFlowEntries();
1431 Iterable<IFlowEntry> outFlowEntries = portObject.getOutFlowEntries();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001432
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001433 //
1434 // Collect all affected Flow IDs from the affected flow entries
1435 //
1436 HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
1437 for (IFlowEntry flowEntryObj: inFlowEntries) {
1438 IFlowPath flowObj = flowEntryObj.getFlow();
1439 if (flowObj != null)
1440 flowObjSet.add(flowObj);
1441 }
1442 for (IFlowEntry flowEntryObj: outFlowEntries) {
1443 IFlowPath flowObj = flowEntryObj.getFlow();
1444 if (flowObj != null)
1445 flowObjSet.add(flowObj);
1446 }
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001447
1448 // Reconcile the affected flows
1449 reconcileFlows(flowObjSet);
1450 }
1451
1452 /**
1453 * Reconcile all flows in a set.
1454 *
1455 * @param flowObjSet the set of flows that need to be reconciliated.
1456 */
1457 public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
1458 if (! flowObjSet.iterator().hasNext())
1459 return;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001460
1461 //
1462 // Remove the old Flow Entries, and add the new Flow Entries
1463 //
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001464
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001465 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001466 LinkedList<FlowPath> flowPaths = new LinkedList<FlowPath>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001467 for (IFlowPath flowObj : flowObjSet) {
1468 FlowPath flowPath = extractFlowPath(flowObj);
1469 if (flowPath == null)
1470 continue;
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001471 flowPaths.add(flowPath);
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001472
1473 //
1474 // Remove my Flow Entries from the Network MAP
1475 //
1476 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001477 LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001478 for (IFlowEntry flowEntryObj : flowEntries) {
1479 String dpidStr = flowEntryObj.getSwitchDpid();
1480 if (dpidStr == null)
1481 continue;
1482 Dpid dpid = new Dpid(dpidStr);
1483 IOFSwitch mySwitch = mySwitches.get(dpid.value());
1484 if (mySwitch == null)
1485 continue; // Ignore the entry: not my switch
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001486 deleteFlowEntries.add(flowEntryObj);
1487 }
1488 for (IFlowEntry flowEntryObj : deleteFlowEntries) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001489 flowObj.removeFlowEntry(flowEntryObj);
1490 conn.utils().removeFlowEntry(conn, flowEntryObj);
1491 }
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001492 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001493
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001494 for (FlowPath flowPath : flowPaths) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001495 //
1496 // Delete the flow entries from the switches
1497 //
1498 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
1499 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001500 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1501 if (mySwitch == null) {
1502 // Not my switch
1503 installRemoteFlowEntry(flowEntry);
1504 } else {
1505 installFlowEntry(mySwitch, flowEntry);
1506 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001507 }
1508
1509 //
1510 // Calculate the new shortest path and install it in the
1511 // Network MAP.
1512 //
1513 FlowPath addedFlowPath = addAndMaintainShortestPathFlow(flowPath);
1514 if (addedFlowPath == null) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001515 log.error("Cannot add Shortest Path Flow from {} to {}: path not found?",
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001516 flowPath.dataPath().srcPort().toString(),
1517 flowPath.dataPath().dstPort().toString());
1518 continue;
1519 }
1520
1521 //
1522 // Add the flow entries to the switches
1523 //
1524 for (FlowEntry flowEntry : addedFlowPath.dataPath().flowEntries()) {
1525 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001526 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1527 if (mySwitch == null) {
1528 // Not my switch
1529 installRemoteFlowEntry(flowEntry);
1530 continue;
1531 }
1532
1533 IFlowEntry flowEntryObj =
1534 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId());
1535 if (flowEntryObj == null) {
1536 //
1537 // TODO: Remove the "new Object[] wrapper in the statement
1538 // below after the SLF4J logger is upgraded to
1539 // Version 1.7.5
1540 //
1541 log.error("Cannot add Flow Entry to switch {} for Path Flow from {} to {} : Flow Entry not in the Network MAP",
1542 new Object[] {
1543 flowEntry.dpid(),
1544 flowPath.dataPath().srcPort(),
1545 flowPath.dataPath().dstPort()
1546 });
1547 continue;
1548 }
1549 // Update the Flow Entry Switch State in the Network MAP
1550 if (installFlowEntry(mySwitch, flowEntry)) {
1551 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1552 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001553 }
1554 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001555 }
1556
1557 /**
1558 * Reconcile all flows between a source and a destination port.
1559 *
1560 * TODO: We don't need it for now.
1561 *
1562 * @param src_port the source port.
1563 * @param dest_port the destination port.
1564 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001565 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001566 public void reconcileFlow(IPortObject src_port, IPortObject dest_port) {
1567 // TODO: We don't need it for now.
1568 }
1569
1570 /**
1571 * Compute the shortest path between a source and a destination ports.
1572 *
1573 * @param src_port the source port.
1574 * @param dest_port the destination port.
1575 * @return the computed shortest path between the source and the
1576 * destination ports. The flow entries in the path itself would
1577 * contain the incoming port matching and the outgoing port output
1578 * actions set. However, the path itself will NOT have the Flow ID,
1579 * Installer ID, and any additional matching conditions for the
1580 * flow entries (e.g., source or destination MAC address, etc).
1581 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001582 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001583 public FlowPath computeFlowPath(IPortObject src_port,
1584 IPortObject dest_port) {
1585 //
1586 // Prepare the arguments
1587 //
1588 String dpidStr = src_port.getSwitch().getDPID();
1589 Dpid srcDpid = new Dpid(dpidStr);
1590 Port srcPort = new Port(src_port.getNumber());
1591
1592 dpidStr = dest_port.getSwitch().getDPID();
1593 Dpid dstDpid = new Dpid(dpidStr);
1594 Port dstPort = new Port(dest_port.getNumber());
1595
1596 SwitchPort src = new SwitchPort(srcDpid, srcPort);
1597 SwitchPort dst = new SwitchPort(dstDpid, dstPort);
1598
1599 //
1600 // Do the shortest path computation
1601 //
1602 DataPath dataPath = topoRouteService.getShortestPath(src, dst);
1603 if (dataPath == null)
1604 return null;
1605
1606 //
1607 // Set the incoming port matching and the outgoing port output
1608 // actions for each flow entry.
1609 //
1610 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1611 // Set the incoming port matching
1612 FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
1613 if (flowEntryMatch == null) {
1614 flowEntryMatch = new FlowEntryMatch();
1615 flowEntry.setFlowEntryMatch(flowEntryMatch);
1616 }
1617 flowEntryMatch.enableInPort(flowEntry.inPort());
1618
1619 // Set the outgoing port output action
1620 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1621 if (flowEntryActions == null) {
1622 flowEntryActions = new ArrayList<FlowEntryAction>();
1623 flowEntry.setFlowEntryActions(flowEntryActions);
1624 }
1625 FlowEntryAction flowEntryAction = new FlowEntryAction();
1626 flowEntryAction.setActionOutput(flowEntry.outPort());
1627 flowEntryActions.add(flowEntryAction);
1628 }
1629
1630 //
1631 // Prepare the return result
1632 //
1633 FlowPath flowPath = new FlowPath();
1634 flowPath.setDataPath(dataPath);
1635
1636 return flowPath;
1637 }
1638
1639 /**
1640 * Get all Flow Entries of a Flow.
1641 *
1642 * @param flow the flow whose flow entries should be returned.
1643 * @return the flow entries of the flow.
1644 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001645 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001646 public Iterable<FlowEntry> getFlowEntries(FlowPath flow) {
1647 return flow.dataPath().flowEntries();
1648 }
1649
1650 /**
1651 * Install a Flow Entry on a switch.
1652 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001653 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001654 * @param flowEntry the flow entry to install.
1655 * @return true on success, otherwise false.
1656 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001657 @Override
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001658 public boolean installFlowEntry(IOFSwitch mySwitch, FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001659 //
1660 // Create the OpenFlow Flow Modification Entry to push
1661 //
1662 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1663 .getMessage(OFType.FLOW_MOD);
1664 long cookie = flowEntry.flowEntryId().value();
1665
1666 short flowModCommand = OFFlowMod.OFPFC_ADD;
1667 if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
1668 flowModCommand = OFFlowMod.OFPFC_ADD;
1669 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
1670 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1671 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
1672 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1673 } else {
1674 // Unknown user state. Ignore the entry
1675 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1676 flowEntry.flowEntryId().toString(),
1677 flowEntry.flowEntryUserState());
1678 return false;
1679 }
1680
1681 //
1682 // Fetch the match conditions
1683 //
1684 OFMatch match = new OFMatch();
1685 match.setWildcards(OFMatch.OFPFW_ALL);
1686 Port matchInPort = flowEntry.flowEntryMatch().inPort();
1687 if (matchInPort != null) {
1688 match.setInputPort(matchInPort.value());
1689 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1690 }
1691 Short matchEthernetFrameType =
1692 flowEntry.flowEntryMatch().ethernetFrameType();
1693 if (matchEthernetFrameType != null) {
1694 match.setDataLayerType(matchEthernetFrameType);
1695 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1696 }
1697 IPv4Net matchSrcIPv4Net = flowEntry.flowEntryMatch().srcIPv4Net();
1698 if (matchSrcIPv4Net != null) {
1699 match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
1700 }
1701 IPv4Net matchDstIPv4Net = flowEntry.flowEntryMatch().dstIPv4Net();
1702 if (matchDstIPv4Net != null) {
1703 match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
1704 }
1705 MACAddress matchSrcMac = flowEntry.flowEntryMatch().srcMac();
1706 if (matchSrcMac != null) {
1707 match.setDataLayerSource(matchSrcMac.toString());
1708 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1709 }
1710 MACAddress matchDstMac = flowEntry.flowEntryMatch().dstMac();
1711 if (matchDstMac != null) {
1712 match.setDataLayerDestination(matchDstMac.toString());
1713 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1714 }
1715
1716 //
1717 // Fetch the actions
1718 //
1719 // TODO: For now we support only the "OUTPUT" actions.
1720 //
1721 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1722 List<OFAction> actions = new ArrayList<OFAction>();
1723 ArrayList<FlowEntryAction> flowEntryActions =
1724 flowEntry.flowEntryActions();
1725 for (FlowEntryAction flowEntryAction : flowEntryActions) {
1726 FlowEntryAction.ActionOutput actionOutput =
1727 flowEntryAction.actionOutput();
1728 if (actionOutput != null) {
1729 short actionOutputPort = actionOutput.port().value();
1730 OFActionOutput action = new OFActionOutput();
1731 // XXX: The max length is hard-coded for now
1732 action.setMaxLength((short)0xffff);
1733 action.setPort(actionOutputPort);
1734 actions.add(action);
1735 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1736 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1737 fm.setOutPort(actionOutputPort);
1738 }
1739 }
1740 }
1741
1742 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1743 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1744 .setPriority(PRIORITY_DEFAULT)
1745 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1746 .setCookie(cookie)
1747 .setCommand(flowModCommand)
1748 .setMatch(match)
1749 .setActions(actions)
1750 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1751
1752 //
1753 // TODO: Set the following flag
1754 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1755 // See method ForwardingBase::pushRoute()
1756 //
1757
1758 //
1759 // Write the message to the switch
1760 //
1761 try {
1762 messageDamper.write(mySwitch, fm, null);
1763 mySwitch.flush();
1764 } catch (IOException e) {
1765 log.error("Failure writing flow mod from network map", e);
1766 return false;
1767 }
1768 return true;
1769 }
1770
1771 /**
1772 * Remove a Flow Entry from a switch.
1773 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001774 * @param mySwitch the switch to remove the Flow Entry from.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001775 * @param flowEntry the flow entry to remove.
1776 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001777 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001778 @Override
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001779 public boolean removeFlowEntry(IOFSwitch mySwitch, FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001780 //
1781 // The installFlowEntry() method implements both installation
1782 // and removal of flow entries.
1783 //
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001784 return (installFlowEntry(mySwitch, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001785 }
1786
1787 /**
1788 * Install a Flow Entry on a remote controller.
1789 *
1790 * TODO: We need it now: Jono
1791 * - For now it will make a REST call to the remote controller.
1792 * - Internally, it needs to know the name of the remote controller.
1793 *
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001794 * @param flowEntry the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001795 * @return true on success, otherwise false.
1796 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001797 @Override
1798 public boolean installRemoteFlowEntry(FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001799 // TODO: We need it now: Jono
1800 // - For now it will make a REST call to the remote controller.
1801 // - Internally, it needs to know the name of the remote controller.
1802 return true;
1803 }
1804
1805 /**
1806 * Remove a flow entry on a remote controller.
1807 *
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001808 * @param flowEntry the flow entry to remove.
1809 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001810 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001811 @Override
1812 public boolean removeRemoteFlowEntry(FlowEntry flowEntry) {
1813 //
1814 // The installRemoteFlowEntry() method implements both installation
1815 // and removal of flow entries.
1816 //
1817 return (installRemoteFlowEntry(flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001818 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001819}