blob: 2b9bf682feed2ac06158243765466abe939bccfe [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 Radoslavov0b22d0e2013-04-02 01:12:46 +000012import java.util.Random;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070013import java.util.TreeMap;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080014import java.util.concurrent.Executors;
15import java.util.concurrent.ScheduledExecutorService;
16import java.util.concurrent.ScheduledFuture;
17import java.util.concurrent.TimeUnit;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080018
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080019import net.floodlightcontroller.core.IFloodlightProviderService;
20import net.floodlightcontroller.core.INetMapStorage;
21import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
22import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070023import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
Pankaj Berded0079742013-03-27 17:53:25 -070024import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070025import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080026import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080027import net.floodlightcontroller.core.module.FloodlightModuleContext;
28import net.floodlightcontroller.core.module.FloodlightModuleException;
29import net.floodlightcontroller.core.module.IFloodlightModule;
30import net.floodlightcontroller.core.module.IFloodlightService;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080031import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
32import net.floodlightcontroller.restserver.IRestApiService;
33import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080034import net.floodlightcontroller.util.DataPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080035import net.floodlightcontroller.util.DataPathEndpoints;
Jonathan Hart01f2d272013-04-04 20:03:46 -070036import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080037import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070038import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080039import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070040import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080041import net.floodlightcontroller.util.FlowEntrySwitchState;
42import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080043import net.floodlightcontroller.util.FlowId;
44import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070045import net.floodlightcontroller.util.IPv4Net;
46import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080047import net.floodlightcontroller.util.OFMessageDamper;
48import net.floodlightcontroller.util.Port;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070049import net.floodlightcontroller.util.SwitchPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080050import net.onrc.onos.util.GraphDBConnection;
51import net.onrc.onos.util.GraphDBConnection.Transaction;
52
53import org.openflow.protocol.OFFlowMod;
54import org.openflow.protocol.OFMatch;
55import org.openflow.protocol.OFPacketOut;
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070056import org.openflow.protocol.OFPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080057import org.openflow.protocol.OFType;
58import org.openflow.protocol.action.OFAction;
59import org.openflow.protocol.action.OFActionOutput;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080060import org.slf4j.Logger;
61import org.slf4j.LoggerFactory;
62
Pavlin Radoslavov5adf1522013-04-04 17:43:41 -070063public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080064
65 public GraphDBConnection conn;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080066
67 protected IRestApiService restApi;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080068 protected IFloodlightProviderService floodlightProvider;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070069 protected ITopoRouteService topoRouteService;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070070 protected FloodlightModuleContext context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080071
72 protected OFMessageDamper messageDamper;
73
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070074 //
75 // TODO: Values copied from elsewhere (class LearningSwitch).
76 // The local copy should go away!
77 //
78 protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
79 protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
80 public static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0; // infinity
81 public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
82 public static final short PRIORITY_DEFAULT = 100;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080083
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +000084 // Flow Entry ID generation state
85 private static Random randomGenerator = new Random();
86 private static int nextFlowEntryIdPrefix = 0;
87 private static int nextFlowEntryIdSuffix = 0;
88 private static long nextFlowEntryId = 0;
89
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070090 private static long measurementFlowId = 100000;
91 private static String measurementFlowIdStr = "0x186a0"; // 100000
92 private long modifiedMeasurementFlowTime = 0;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070093
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080094 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080095 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
96
97 // The periodic task(s)
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070098 private final ScheduledExecutorService mapReaderScheduler =
99 Executors.newScheduledThreadPool(1);
100
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700101 final Runnable mapReader = new Runnable() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800102 public void run() {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700103 long startTime = System.nanoTime();
104 int counterAllFlowEntries = 0;
105 int counterMyNotUpdatedFlowEntries = 0;
106 int counterAllFlowPaths = 0;
107 int counterMyFlowPaths = 0;
108
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800109 if (floodlightProvider == null) {
110 log.debug("FloodlightProvider service not found!");
111 return;
112 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000113 Map<Long, IOFSwitch> mySwitches =
114 floodlightProvider.getSwitches();
115 Map<Long, IFlowEntry> myFlowEntries =
116 new TreeMap<Long, IFlowEntry>();
117 LinkedList<IFlowEntry> deleteFlowEntries =
118 new LinkedList<IFlowEntry>();
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700119
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700120
121 //
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700122 // Fetch all Flow Entries and select only my Flow Entries
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700123 // that need to be updated into the switches.
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700124 //
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000125 Iterable<IFlowEntry> allFlowEntries =
126 conn.utils().getAllFlowEntries(conn);
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700127 for (IFlowEntry flowEntryObj : allFlowEntries) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700128 counterAllFlowEntries++;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000129 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700130 String userState = flowEntryObj.getUserState();
131 String switchState = flowEntryObj.getSwitchState();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000132 String dpidStr = flowEntryObj.getSwitchDpid();
133 if ((flowEntryIdStr == null) ||
134 (userState == null) ||
135 (switchState == null) ||
136 (dpidStr == null)) {
137 log.debug("IGNORING Flow Entry entry with null fields");
138 continue;
139 }
140 FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
141 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800142
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000143 if (! switchState.equals("FE_SWITCH_NOT_UPDATED"))
144 continue; // Ignore the entry: nothing to do
145
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800146 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000147 if (mySwitch == null)
148 continue; // Ignore the entry: not my switch
149
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700150 myFlowEntries.put(flowEntryId.value(), flowEntryObj);
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700151 if (userState.equals("FE_USER_DELETE")) {
152 // An entry that needs to be deleted.
153 deleteFlowEntries.add(flowEntryObj);
154 }
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700155 }
156
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700157 log.debug("MEASUREMENT: Found {} My Flow Entries NOT_UPDATED",
158 myFlowEntries.size());
159
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700160 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700161 // Process my Flow Entries in the Flow Entry ID order
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700162 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700163 boolean processed_measurement_flow = false;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700164 for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700165 counterMyNotUpdatedFlowEntries++;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700166 IFlowEntry flowEntryObj = entry.getValue();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700167 IFlowPath flowObj =
168 conn.utils().getFlowPathByFlowEntry(conn,
169 flowEntryObj);
170 if (flowObj == null)
171 continue; // Should NOT happen
172
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700173 // Code for measurement purpose
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700174 // TODO: Commented-out for now
175 /*
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700176 {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700177 if (flowObj.getFlowId().equals(measurementFlowIdStr)) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700178 processed_measurement_flow = true;
179 }
180 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700181 */
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700182
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700183 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700184 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000185 if (mySwitch == null)
186 continue; // Shouldn't happen
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -0700187 installFlowEntry(mySwitch, flowObj, flowEntryObj);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800188 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000189
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700190 log.debug("MEASUREMENT: Found {} Flow Entries to delete",
191 deleteFlowEntries.size());
192
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000193 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -0700194 // Delete all entries marked for deletion from the
195 // Network MAP.
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000196 //
197 // TODO: We should use the OpenFlow Barrier mechanism
198 // to check for errors, and delete the Flow Entries after the
199 // Barrier message is received.
200 //
201 while (! deleteFlowEntries.isEmpty()) {
202 IFlowEntry flowEntryObj = deleteFlowEntries.poll();
203 IFlowPath flowObj =
204 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
205 if (flowObj == null) {
206 log.debug("Did not find FlowPath to be deleted");
207 continue;
208 }
209 flowObj.removeFlowEntry(flowEntryObj);
210 conn.utils().removeFlowEntry(conn, flowEntryObj);
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000211 }
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700212
213
214 //
215 // Fetch and recompute the Shortest Path for those
216 // Flow Paths this controller is responsible for.
217 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700218 topoRouteService.prepareShortestPathTopo();
219 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
220 HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
221 for (IFlowPath flowPathObj : allFlowPaths) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700222 counterAllFlowPaths++;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700223 if (flowPathObj == null)
224 continue;
225 String dataPathSummaryStr = flowPathObj.getDataPathSummary();
226 if (dataPathSummaryStr == null)
227 continue; // Could be invalid entry?
228 if (dataPathSummaryStr.isEmpty())
229 continue; // No need to maintain this flow
230
231 // Fetch the fields needed to recompute the shortest path
232 String flowIdStr = flowPathObj.getFlowId();
233 String srcDpidStr = flowPathObj.getSrcSwitch();
234 Short srcPortShort = flowPathObj.getSrcPort();
235 String dstDpidStr = flowPathObj.getDstSwitch();
236 Short dstPortShort = flowPathObj.getDstPort();
237 if ((flowIdStr == null) ||
238 (srcDpidStr == null) ||
239 (srcPortShort == null) ||
240 (dstDpidStr == null) ||
241 (dstPortShort == null)) {
242 log.debug("IGNORING Flow Path entry with null fields");
243 continue;
244 }
245
246 FlowId flowId = new FlowId(flowIdStr);
247 Dpid srcDpid = new Dpid(srcDpidStr);
248 Port srcPort = new Port(srcPortShort);
249 Dpid dstDpid = new Dpid(dstDpidStr);
250 Port dstPort = new Port(dstPortShort);
251 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
252 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
Pavlin Radoslavov2659a0b2013-04-03 20:30:40 -0700253
254 //
255 // Use the source DPID as a heuristic to decide
256 // which controller is responsible for maintaining the
257 // shortest path.
258 // NOTE: This heuristic is error-prone: if the switch
259 // goes away and no controller is responsible for that
260 // switch, then the original Flow Path is not cleaned-up
261 //
262 IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
263 if (mySwitch == null)
264 continue; // Ignore: not my responsibility
265
266 counterMyFlowPaths++;
267
Pavlin Radoslavov832aa652013-03-29 16:21:59 -0700268 //
269 // NOTE: Using here the regular getShortestPath() method
270 // won't work here, because that method calls internally
271 // "conn.endTx(Transaction.COMMIT)", and that will
272 // invalidate all handlers to the Titan database.
273 // If we want to experiment with calling here
274 // getShortestPath(), we need to refactor that code
275 // to avoid closing the transaction.
276 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700277 DataPath dataPath =
278 topoRouteService.getTopoShortestPath(srcSwitchPort,
279 dstSwitchPort);
Pavlin Radoslavov4a325822013-04-02 22:16:59 +0000280 if (dataPath == null) {
281 // We need the DataPath to compare the paths
282 dataPath = new DataPath();
283 dataPath.setSrcPort(srcSwitchPort);
284 dataPath.setDstPort(dstSwitchPort);
285 }
286
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700287 String newDataPathSummaryStr = dataPath.dataPathSummary();
288 if (dataPathSummaryStr.equals(newDataPathSummaryStr))
289 continue; // Nothing changed
290
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700291 log.debug("RECONCILE: Need to Reconcile Shortest Path for FlowID {}",
292 flowId.toString());
293 flowObjSet.add(flowPathObj);
294 }
Pavlin Radoslavov53a3a8c2013-04-04 04:34:50 -0700295 log.debug("MEASUREMENT: Found {} Flows to reconcile",
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700296 flowObjSet.size());
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700297 reconcileFlows(flowObjSet);
298 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700299
300
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800301 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700302
303 if (processed_measurement_flow) {
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700304 long estimatedTime =
305 System.nanoTime() - modifiedMeasurementFlowTime;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700306 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
307 (double)estimatedTime / 1000000000 + " sec";
308 log.debug(logMsg);
309 }
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700310
311 long estimatedTime = System.nanoTime() - startTime;
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700312 double rate = 0.0;
313 if (estimatedTime > 0)
314 rate = ((double)counterAllFlowPaths * 1000000000) / estimatedTime;
315 String logMsg = "MEASUREMENT: Processed AllFlowEntries: " +
316 counterAllFlowEntries + " MyNotUpdatedFlowEntries: " +
317 counterMyNotUpdatedFlowEntries + " AllFlowPaths: " +
318 counterAllFlowPaths + " MyFlowPaths: " +
319 counterMyFlowPaths + " in " +
320 (double)estimatedTime / 1000000000 + " sec: " +
321 rate + " paths/s";
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700322 log.debug(logMsg);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800323 }
324 };
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700325
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700326 final ScheduledFuture<?> mapReaderHandle =
327 mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800328
329 @Override
330 public void init(String conf) {
331 conn = GraphDBConnection.getInstance(conf);
332 }
333
334 public void finalize() {
335 close();
336 }
337
338 @Override
339 public void close() {
340 conn.close();
341 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800342
343 @Override
344 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
345 Collection<Class<? extends IFloodlightService>> l =
346 new ArrayList<Class<? extends IFloodlightService>>();
347 l.add(IFlowService.class);
348 return l;
349 }
350
351 @Override
352 public Map<Class<? extends IFloodlightService>, IFloodlightService>
353 getServiceImpls() {
354 Map<Class<? extends IFloodlightService>,
355 IFloodlightService> m =
356 new HashMap<Class<? extends IFloodlightService>,
357 IFloodlightService>();
358 m.put(IFlowService.class, this);
359 return m;
360 }
361
362 @Override
363 public Collection<Class<? extends IFloodlightService>>
364 getModuleDependencies() {
365 Collection<Class<? extends IFloodlightService>> l =
366 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800367 l.add(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700368 l.add(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800369 l.add(IRestApiService.class);
370 return l;
371 }
372
373 @Override
374 public void init(FloodlightModuleContext context)
375 throws FloodlightModuleException {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700376 this.context = context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800377 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700378 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800379 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800380 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
381 EnumSet.of(OFType.FLOW_MOD),
382 OFMESSAGE_DAMPER_TIMEOUT);
383 // TODO: An ugly hack!
384 String conf = "/tmp/cassandra.titan";
385 this.init(conf);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800386 }
387
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000388 private long getNextFlowEntryId() {
389 //
390 // Generate the next Flow Entry ID.
391 // NOTE: For now, the higher 32 bits are random, and
392 // the lower 32 bits are sequential.
393 // In the future, we need a better allocation mechanism.
394 //
395 if ((nextFlowEntryIdSuffix & 0xffffffffL) == 0xffffffffL) {
396 nextFlowEntryIdPrefix = randomGenerator.nextInt();
397 nextFlowEntryIdSuffix = 0;
398 } else {
399 nextFlowEntryIdSuffix++;
400 }
401 long result = (long)nextFlowEntryIdPrefix << 32;
402 result = result | (0xffffffffL & nextFlowEntryIdSuffix);
403 return result;
404 }
405
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800406 @Override
407 public void startUp(FloodlightModuleContext context) {
408 restApi.addRestletRoutable(new FlowWebRoutable());
Pavlin Radoslavov80ca6302013-03-20 02:08:09 -0700409
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000410 // Initialize the Flow Entry ID generator
411 nextFlowEntryIdPrefix = randomGenerator.nextInt();
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800412 }
413
414 /**
415 * Add a flow.
416 *
417 * Internally, ONOS will automatically register the installer for
418 * receiving Flow Path Notifications for that path.
419 *
420 * @param flowPath the Flow Path to install.
421 * @param flowId the return-by-reference Flow ID as assigned internally.
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700422 * @param dataPathSummaryStr the data path summary string if the added
423 * flow will be maintained internally, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800424 * @return true on success, otherwise false.
425 */
426 @Override
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700427 public boolean addFlow(FlowPath flowPath, FlowId flowId,
428 String dataPathSummaryStr) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700429 /*
430 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700431 if (flowPath.flowId().value() == measurementFlowId) {
432 modifiedMeasurementFlowTime = System.nanoTime();
433 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700434 */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800435
436 IFlowPath flowObj = null;
437 try {
438 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
439 != null) {
440 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
441 flowPath.flowId().toString());
442 } else {
443 flowObj = conn.utils().newFlowPath(conn);
444 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
445 flowPath.flowId().toString());
446 }
447 } catch (Exception e) {
448 // TODO: handle exceptions
449 conn.endTx(Transaction.ROLLBACK);
450 log.error(":addFlow FlowId:{} failed",
451 flowPath.flowId().toString());
452 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700453 if (flowObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000454 log.error(":addFlow FlowId:{} failed: Flow object not created",
455 flowPath.flowId().toString());
456 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800457 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700458 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800459
460 //
461 // Set the Flow key:
462 // - flowId
463 //
464 flowObj.setFlowId(flowPath.flowId().toString());
465 flowObj.setType("flow");
466
467 //
468 // Set the Flow attributes:
469 // - flowPath.installerId()
470 // - flowPath.dataPath().srcPort()
471 // - flowPath.dataPath().dstPort()
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700472 // - flowPath.matchEthernetFrameType()
473 // - flowPath.matchSrcIPv4Net()
474 // - flowPath.matchDstIPv4Net()
475 // - flowPath.matchSrcMac()
476 // - flowPath.matchDstMac()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800477 //
478 flowObj.setInstallerId(flowPath.installerId().toString());
479 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
480 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
481 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
482 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700483 if (flowPath.flowEntryMatch().matchEthernetFrameType()) {
484 flowObj.setMatchEthernetFrameType(flowPath.flowEntryMatch().ethernetFrameType());
485 }
486 if (flowPath.flowEntryMatch().matchSrcIPv4Net()) {
487 flowObj.setMatchSrcIPv4Net(flowPath.flowEntryMatch().srcIPv4Net().toString());
488 }
489 if (flowPath.flowEntryMatch().matchDstIPv4Net()) {
490 flowObj.setMatchDstIPv4Net(flowPath.flowEntryMatch().dstIPv4Net().toString());
491 }
492 if (flowPath.flowEntryMatch().matchSrcMac()) {
493 flowObj.setMatchSrcMac(flowPath.flowEntryMatch().srcMac().toString());
494 }
495 if (flowPath.flowEntryMatch().matchDstMac()) {
496 flowObj.setMatchDstMac(flowPath.flowEntryMatch().dstMac().toString());
497 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800498
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700499 if (dataPathSummaryStr != null) {
500 flowObj.setDataPathSummary(dataPathSummaryStr);
501 } else {
502 flowObj.setDataPathSummary("");
503 }
504
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800505 // Flow edges:
506 // HeadFE
507
508
509 //
510 // Flow Entries:
511 // flowPath.dataPath().flowEntries()
512 //
513 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700514 if (addFlowEntry(flowObj, flowEntry) != true) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000515 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800516 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700517 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800518 }
519 conn.endTx(Transaction.COMMIT);
520
521 //
522 // TODO: We need a proper Flow ID allocation mechanism.
523 //
524 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700525
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800526 return true;
527 }
528
529 /**
Pavlin Radoslavov9425f702013-04-04 19:55:07 -0700530 * Add a flow entry to the Network MAP.
531 *
532 * @param flowObj the corresponding Flow Path object for the Flow Entry.
533 * @param flowEntry the Flow Entry to install.
534 * @return true on success, otherwise false.
535 */
536 private boolean addFlowEntry(IFlowPath flowObj, FlowEntry flowEntry) {
537 // Flow edges
538 // HeadFE (TODO)
539
540 //
541 // Assign the FlowEntry ID.
542 //
543 if ((flowEntry.flowEntryId() == null) ||
544 (flowEntry.flowEntryId().value() == 0)) {
545 long id = getNextFlowEntryId();
546 flowEntry.setFlowEntryId(new FlowEntryId(id));
547 }
548
549 IFlowEntry flowEntryObj = null;
550 boolean found = false;
551 try {
552 if ((flowEntryObj =
553 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
554 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
555 flowEntry.flowEntryId().toString());
556 found = true;
557 } else {
558 flowEntryObj = conn.utils().newFlowEntry(conn);
559 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
560 flowEntry.flowEntryId().toString());
561 }
562 } catch (Exception e) {
563 log.error(":addFlow FlowEntryId:{} failed",
564 flowEntry.flowEntryId().toString());
565 return false;
566 }
567 if (flowEntryObj == null) {
568 log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created",
569 flowEntry.flowEntryId().toString());
570 return false;
571 }
572
573 //
574 // Set the Flow Entry key:
575 // - flowEntry.flowEntryId()
576 //
577 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
578 flowEntryObj.setType("flow_entry");
579
580 //
581 // Set the Flow Entry Edges and attributes:
582 // - Switch edge
583 // - InPort edge
584 // - OutPort edge
585 //
586 // - flowEntry.flowEntryMatch()
587 // - flowEntry.flowEntryActions()
588 // - flowEntry.dpid()
589 // - flowEntry.flowEntryUserState()
590 // - flowEntry.flowEntrySwitchState()
591 // - flowEntry.flowEntryErrorState()
592 // - flowEntry.matchInPort()
593 // - flowEntry.matchEthernetFrameType()
594 // - flowEntry.matchSrcIPv4Net()
595 // - flowEntry.matchDstIPv4Net()
596 // - flowEntry.matchSrcMac()
597 // - flowEntry.matchDstMac()
598 // - flowEntry.actionOutput()
599 //
600 ISwitchObject sw =
601 conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
602 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
603 flowEntryObj.setSwitch(sw);
604 if (flowEntry.flowEntryMatch().matchInPort()) {
605 IPortObject inport =
606 conn.utils().searchPort(conn, flowEntry.dpid().toString(),
607 flowEntry.flowEntryMatch().inPort().value());
608 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
609 flowEntryObj.setInPort(inport);
610 }
611 if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
612 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
613 }
614 if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
615 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
616 }
617 if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
618 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
619 }
620 if (flowEntry.flowEntryMatch().matchSrcMac()) {
621 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
622 }
623 if (flowEntry.flowEntryMatch().matchDstMac()) {
624 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
625 }
626
627 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
628 if (fa.actionOutput() != null) {
629 IPortObject outport =
630 conn.utils().searchPort(conn,
631 flowEntry.dpid().toString(),
632 fa.actionOutput().port().value());
633 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
634 flowEntryObj.setOutPort(outport);
635 }
636 }
637 // TODO: Hacks with hard-coded state names!
638 if (found)
639 flowEntryObj.setUserState("FE_USER_MODIFY");
640 else
641 flowEntryObj.setUserState("FE_USER_ADD");
642 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
643 //
644 // TODO: Take care of the FlowEntryErrorState.
645 //
646
647 // Flow Entries edges:
648 // Flow
649 // NextFE (TODO)
650 if (! found) {
651 flowObj.addFlowEntry(flowEntryObj);
652 flowEntryObj.setFlow(flowObj);
653 }
654
655 return true;
656 }
657
658 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800659 * Delete a previously added flow.
660 *
661 * @param flowId the Flow ID of the flow to delete.
662 * @return true on success, otherwise false.
663 */
664 @Override
665 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700666 /*
667 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700668 if (flowId.value() == measurementFlowId) {
669 modifiedMeasurementFlowTime = System.nanoTime();
670 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700671 */
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700672
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800673 IFlowPath flowObj = null;
674 //
675 // We just mark the entries for deletion,
676 // and let the switches remove each individual entry after
677 // it has been removed from the switches.
678 //
679 try {
680 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
681 != null) {
682 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
683 flowId.toString());
684 } else {
685 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
686 flowId.toString());
687 }
688 } catch (Exception e) {
689 // TODO: handle exceptions
690 conn.endTx(Transaction.ROLLBACK);
691 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
692 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700693 if (flowObj == null) {
694 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800695 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700696 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800697
698 //
699 // Find and mark for deletion all Flow Entries
700 //
701 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
702 boolean empty = true; // TODO: an ugly hack
703 for (IFlowEntry flowEntryObj : flowEntries) {
704 empty = false;
705 // flowObj.removeFlowEntry(flowEntryObj);
706 // conn.utils().removeFlowEntry(conn, flowEntryObj);
707 flowEntryObj.setUserState("FE_USER_DELETE");
708 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
709 }
710 // Remove from the database empty flows
711 if (empty)
712 conn.utils().removeFlowPath(conn, flowObj);
713 conn.endTx(Transaction.COMMIT);
714
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800715 return true;
716 }
717
718 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700719 * Clear the state for a previously added flow.
720 *
721 * @param flowId the Flow ID of the flow to clear.
722 * @return true on success, otherwise false.
723 */
724 @Override
725 public boolean clearFlow(FlowId flowId) {
726 IFlowPath flowObj = null;
727 try {
728 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
729 != null) {
730 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
731 flowId.toString());
732 } else {
733 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
734 flowId.toString());
735 }
736 } catch (Exception e) {
737 // TODO: handle exceptions
738 conn.endTx(Transaction.ROLLBACK);
739 log.error(":clearFlow FlowId:{} failed", flowId.toString());
740 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700741 if (flowObj == null) {
742 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700743 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700744 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700745
746 //
747 // Remove all Flow Entries
748 //
749 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
750 for (IFlowEntry flowEntryObj : flowEntries) {
751 flowObj.removeFlowEntry(flowEntryObj);
752 conn.utils().removeFlowEntry(conn, flowEntryObj);
753 }
754 // Remove the Flow itself
755 conn.utils().removeFlowPath(conn, flowObj);
756 conn.endTx(Transaction.COMMIT);
757
758 return true;
759 }
760
761 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800762 * Get a previously added flow.
763 *
764 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800765 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800766 */
767 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800768 public FlowPath getFlow(FlowId flowId) {
769 IFlowPath flowObj = null;
770 try {
771 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
772 != null) {
773 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
774 flowId.toString());
775 } else {
776 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
777 flowId.toString());
778 }
779 } catch (Exception e) {
780 // TODO: handle exceptions
781 conn.endTx(Transaction.ROLLBACK);
782 log.error(":getFlow FlowId:{} failed", flowId.toString());
783 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700784 if (flowObj == null) {
785 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800786 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700787 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800788
789 //
790 // Extract the Flow state
791 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800792 FlowPath flowPath = extractFlowPath(flowObj);
793 conn.endTx(Transaction.COMMIT);
794
795 return flowPath;
796 }
797
798 /**
799 * Get all previously added flows by a specific installer for a given
800 * data path endpoints.
801 *
802 * @param installerId the Caller ID of the installer of the flow to get.
803 * @param dataPathEndpoints the data path endpoints of the flow to get.
804 * @return the Flow Paths if found, otherwise null.
805 */
806 @Override
807 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
808 DataPathEndpoints dataPathEndpoints) {
809 //
810 // TODO: The implementation below is not optimal:
811 // We fetch all flows, and then return only the subset that match
812 // the query conditions.
813 // We should use the appropriate Titan/Gremlin query to filter-out
814 // the flows as appropriate.
815 //
816 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700817 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800818
819 if (allFlows == null) {
820 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700821 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800822 }
823
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800824 for (FlowPath flow : allFlows) {
825 //
826 // TODO: String-based comparison is sub-optimal.
827 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800828 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800829 //
830 if (! flow.installerId().toString().equals(installerId.toString()))
831 continue;
832 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
833 continue;
834 }
835 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
836 continue;
837 }
838 flowPaths.add(flow);
839 }
840
841 if (flowPaths.isEmpty()) {
842 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800843 } else {
844 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
845 }
846
847 return flowPaths;
848 }
849
850 /**
851 * Get all installed flows by all installers for given data path endpoints.
852 *
853 * @param dataPathEndpoints the data path endpoints of the flows to get.
854 * @return the Flow Paths if found, otherwise null.
855 */
856 @Override
857 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
858 //
859 // TODO: The implementation below is not optimal:
860 // We fetch all flows, and then return only the subset that match
861 // the query conditions.
862 // We should use the appropriate Titan/Gremlin query to filter-out
863 // the flows as appropriate.
864 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700865 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
866 ArrayList<FlowPath> allFlows = getAllFlows();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800867
868 if (allFlows == null) {
869 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700870 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800871 }
872
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800873 for (FlowPath flow : allFlows) {
874 //
875 // TODO: String-based comparison is sub-optimal.
876 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800877 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800878 //
879 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
880 continue;
881 }
882 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
883 continue;
884 }
885 flowPaths.add(flow);
886 }
887
888 if (flowPaths.isEmpty()) {
889 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800890 } else {
891 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
892 }
893
894 return flowPaths;
895 }
896
897 /**
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700898 * Get summary of all installed flows by all installers in a given range
899 *
900 * @param flowId the data path endpoints of the flows to get.
901 * @param maxFlows: the maximum number of flows to be returned
902 * @return the Flow Paths if found, otherwise null.
903 */
904 @Override
Jonathan Hart01f2d272013-04-04 20:03:46 -0700905 public ArrayList<IFlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
906
907
908
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700909 //
910 // TODO: The implementation below is not optimal:
911 // We fetch all flows, and then return only the subset that match
912 // the query conditions.
913 // We should use the appropriate Titan/Gremlin query to filter-out
914 // the flows as appropriate.
915 //
Jonathan Hart01f2d272013-04-04 20:03:46 -0700916 //ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700917
Jonathan Hart01f2d272013-04-04 20:03:46 -0700918 ArrayList<IFlowPath> flowPathsWithoutFlowEntries = getAllFlowsWithoutFlowEntries();
919
920 return flowPathsWithoutFlowEntries;
921
922 /*
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700923 ArrayList<FlowPath> allFlows = getAllFlows();
Jonathan Hart01f2d272013-04-04 20:03:46 -0700924
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700925 if (allFlows == null) {
926 log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700927 return flowPaths;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700928 }
929
Umesh Krishnaswamy244b4ae2013-03-29 12:05:15 -0700930 Collections.sort(allFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700931
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700932 for (FlowPath flow : allFlows) {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700933 flow.setFlowEntryMatch(null);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700934
Pavlin Radoslavov96b43422013-04-04 19:14:56 -0700935 // start from desired flowId
936 if (flow.flowId().value() < flowId.value()) {
937 continue;
938 }
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700939
940 // Summarize by making null flow entry fields that are not relevant to report
941 for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
942 flowEntry.setFlowEntryActions(null);
943 flowEntry.setFlowEntryMatch(null);
944 }
945
946 flowPaths.add(flow);
947 if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
948 break;
949 }
950 }
951
952 if (flowPaths.isEmpty()) {
953 log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700954 } else {
955 log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
956 }
957
958 return flowPaths;
Jonathan Hart01f2d272013-04-04 20:03:46 -0700959 */
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700960 }
961
962 /**
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800963 * Get all installed flows by all installers.
964 *
965 * @return the Flow Paths if found, otherwise null.
966 */
967 @Override
968 public ArrayList<FlowPath> getAllFlows() {
969 Iterable<IFlowPath> flowPathsObj = null;
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700970 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800971
972 try {
973 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
974 log.debug("Get all FlowPaths: found FlowPaths");
975 } else {
976 log.debug("Get all FlowPaths: no FlowPaths found");
977 }
978 } catch (Exception e) {
979 // TODO: handle exceptions
980 conn.endTx(Transaction.ROLLBACK);
981 log.error(":getAllFlowPaths failed");
982 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700983 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
984 conn.endTx(Transaction.COMMIT);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700985 return flowPaths; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700986 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800987
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800988 for (IFlowPath flowObj : flowPathsObj) {
989 //
990 // Extract the Flow state
991 //
992 FlowPath flowPath = extractFlowPath(flowObj);
Pavlin Radoslavov3f2af732013-03-29 15:29:35 -0700993 if (flowPath != null)
994 flowPaths.add(flowPath);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800995 }
996
997 conn.endTx(Transaction.COMMIT);
998
999 return flowPaths;
1000 }
Jonathan Hart01f2d272013-04-04 20:03:46 -07001001
1002 public ArrayList<IFlowPath> getAllFlowsWithoutFlowEntries(){
1003 Iterable<IFlowPath> flowPathsObj = null;
1004 ArrayList<IFlowPath> flowPathsObjArray = new ArrayList<IFlowPath>();
1005 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1006
1007 try {
1008 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
1009 log.debug("Get all FlowPaths: found FlowPaths");
1010 } else {
1011 log.debug("Get all FlowPaths: no FlowPaths found");
1012 }
1013 } catch (Exception e) {
1014 // TODO: handle exceptions
1015 conn.endTx(Transaction.ROLLBACK);
1016 log.error(":getAllFlowPaths failed");
1017 }
1018 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
1019 return new ArrayList<IFlowPath>(); // No Flows found
1020 }
1021
1022 for (IFlowPath flowObj : flowPathsObj){
1023 flowPathsObjArray.add(flowObj);
1024 }
1025 /*
1026 for (IFlowPath flowObj : flowPathsObj) {
1027 //
1028 // Extract the Flow state
1029 //
1030 FlowPath flowPath = extractFlowPath(flowObj);
1031 if (flowPath != null)
1032 flowPaths.add(flowPath);
1033 }
1034 */
1035
1036 //conn.endTx(Transaction.COMMIT);
1037
1038 return flowPathsObjArray;
1039 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001040
1041 /**
1042 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
1043 *
1044 * @param flowObj the object to extract the Flow Path State from.
1045 * @return the extracted Flow Path State.
1046 */
1047 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001048 //
1049 // Extract the Flow state
1050 //
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001051 String flowIdStr = flowObj.getFlowId();
1052 String installerIdStr = flowObj.getInstallerId();
1053 String srcSwitchStr = flowObj.getSrcSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001054 Short srcPortShort = flowObj.getSrcPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001055 String dstSwitchStr = flowObj.getDstSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001056 Short dstPortShort = flowObj.getDstPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001057
1058 if ((flowIdStr == null) ||
1059 (installerIdStr == null) ||
1060 (srcSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001061 (srcPortShort == null) ||
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001062 (dstSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001063 (dstPortShort == null)) {
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001064 // TODO: A work-around, becauuse of some bogus database objects
1065 return null;
1066 }
1067
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001068 FlowPath flowPath = new FlowPath();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001069 flowPath.setFlowId(new FlowId(flowIdStr));
1070 flowPath.setInstallerId(new CallerId(installerIdStr));
1071 flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001072 flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001073 flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001074 flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001075 //
1076 // Extract the match conditions common for all Flow Entries
1077 //
1078 {
1079 FlowEntryMatch match = new FlowEntryMatch();
1080 Short matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1081 if (matchEthernetFrameType != null)
1082 match.enableEthernetFrameType(matchEthernetFrameType);
1083 String matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1084 if (matchSrcIPv4Net != null)
1085 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1086 String matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1087 if (matchDstIPv4Net != null)
1088 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1089 String matchSrcMac = flowObj.getMatchSrcMac();
1090 if (matchSrcMac != null)
1091 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1092 String matchDstMac = flowObj.getMatchDstMac();
1093 if (matchDstMac != null)
1094 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1095 flowPath.setFlowEntryMatch(match);
1096 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001097
1098 //
1099 // Extract all Flow Entries
1100 //
1101 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1102 for (IFlowEntry flowEntryObj : flowEntries) {
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001103 FlowEntry flowEntry = extractFlowEntry(flowEntryObj);
1104 if (flowEntry == null)
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001105 continue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001106 flowPath.dataPath().flowEntries().add(flowEntry);
1107 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001108
1109 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001110 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001111
1112 /**
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001113 * Extract Flow Entry State from a Titan Database Object @ref IFlowEntry.
1114 *
1115 * @param flowEntryObj the object to extract the Flow Entry State from.
1116 * @return the extracted Flow Entry State.
1117 */
1118 private FlowEntry extractFlowEntry(IFlowEntry flowEntryObj) {
1119 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1120 String switchDpidStr = flowEntryObj.getSwitchDpid();
1121 String userState = flowEntryObj.getUserState();
1122 String switchState = flowEntryObj.getSwitchState();
1123
1124 if ((flowEntryIdStr == null) ||
1125 (switchDpidStr == null) ||
1126 (userState == null) ||
1127 (switchState == null)) {
1128 // TODO: A work-around, becauuse of some bogus database objects
1129 return null;
1130 }
1131
1132 FlowEntry flowEntry = new FlowEntry();
1133 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
1134 flowEntry.setDpid(new Dpid(switchDpidStr));
1135
1136 //
1137 // Extract the match conditions
1138 //
1139 FlowEntryMatch match = new FlowEntryMatch();
1140 Short matchInPort = flowEntryObj.getMatchInPort();
1141 if (matchInPort != null)
1142 match.enableInPort(new Port(matchInPort));
1143 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1144 if (matchEthernetFrameType != null)
1145 match.enableEthernetFrameType(matchEthernetFrameType);
1146 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1147 if (matchSrcIPv4Net != null)
1148 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1149 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1150 if (matchDstIPv4Net != null)
1151 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1152 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1153 if (matchSrcMac != null)
1154 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1155 String matchDstMac = flowEntryObj.getMatchDstMac();
1156 if (matchDstMac != null)
1157 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1158 flowEntry.setFlowEntryMatch(match);
1159
1160 //
1161 // Extract the actions
1162 //
1163 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
1164 Short actionOutputPort = flowEntryObj.getActionOutput();
1165 if (actionOutputPort != null) {
1166 FlowEntryAction action = new FlowEntryAction();
1167 action.setActionOutput(new Port(actionOutputPort));
1168 actions.add(action);
1169 }
1170 flowEntry.setFlowEntryActions(actions);
1171 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
1172 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
1173 //
1174 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
1175 // and FlowEntryErrorState.
1176 //
1177 return flowEntry;
1178 }
1179
1180 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001181 * Add and maintain a shortest-path flow.
1182 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001183 * NOTE: The Flow Path argument does NOT contain flow entries.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001184 *
1185 * @param flowPath the Flow Path with the endpoints and the match
1186 * conditions to install.
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001187 * @return the added shortest-path flow on success, otherwise null.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001188 */
1189 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001190 public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001191 String dataPathSummaryStr = null;
1192
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001193 //
1194 // Do the shortest path computation
1195 //
1196 DataPath dataPath =
1197 topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
1198 flowPath.dataPath().dstPort());
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001199 if (dataPath == null) {
1200 // We need the DataPath to populate the Network MAP
1201 dataPath = new DataPath();
1202 dataPath.setSrcPort(flowPath.dataPath().srcPort());
1203 dataPath.setDstPort(flowPath.dataPath().dstPort());
1204 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001205
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001206 // Compute the Data Path summary
1207 dataPathSummaryStr = dataPath.dataPathSummary();
1208
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001209 //
1210 // Set the incoming port matching and the outgoing port output
1211 // actions for each flow entry.
1212 //
1213 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1214 // Set the incoming port matching
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001215 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001216 flowEntry.setFlowEntryMatch(flowEntryMatch);
1217 flowEntryMatch.enableInPort(flowEntry.inPort());
1218
1219 // Set the outgoing port output action
1220 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1221 if (flowEntryActions == null) {
1222 flowEntryActions = new ArrayList<FlowEntryAction>();
1223 flowEntry.setFlowEntryActions(flowEntryActions);
1224 }
1225 FlowEntryAction flowEntryAction = new FlowEntryAction();
1226 flowEntryAction.setActionOutput(flowEntry.outPort());
1227 flowEntryActions.add(flowEntryAction);
1228 }
1229
1230 //
1231 // Prepare the computed Flow Path
1232 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001233 FlowPath computedFlowPath = new FlowPath();
1234 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1235 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1236 computedFlowPath.setDataPath(dataPath);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001237 computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001238
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001239 FlowId flowId = new FlowId();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001240 if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001241 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001242
1243 // TODO: Mark the flow for maintenance purpose
1244
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001245 return (computedFlowPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001246 }
1247
1248 /**
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001249 * Reconcile all flows in a set.
1250 *
1251 * @param flowObjSet the set of flows that need to be reconciliated.
1252 */
1253 public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
1254 if (! flowObjSet.iterator().hasNext())
1255 return;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001256
1257 //
1258 // Remove the old Flow Entries, and add the new Flow Entries
1259 //
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001260
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001261 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001262 LinkedList<FlowPath> flowPaths = new LinkedList<FlowPath>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001263 for (IFlowPath flowObj : flowObjSet) {
1264 FlowPath flowPath = extractFlowPath(flowObj);
1265 if (flowPath == null)
1266 continue;
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001267 flowPaths.add(flowPath);
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001268
1269 //
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001270 // Remove the Flow Entries from the Network MAP
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001271 //
1272 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001273 LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001274 for (IFlowEntry flowEntryObj : flowEntries) {
1275 String dpidStr = flowEntryObj.getSwitchDpid();
1276 if (dpidStr == null)
1277 continue;
1278 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001279 /*
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001280 IOFSwitch mySwitch = mySwitches.get(dpid.value());
1281 if (mySwitch == null)
1282 continue; // Ignore the entry: not my switch
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001283 */
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001284 deleteFlowEntries.add(flowEntryObj);
1285 }
1286 for (IFlowEntry flowEntryObj : deleteFlowEntries) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001287 flowObj.removeFlowEntry(flowEntryObj);
1288 conn.utils().removeFlowEntry(conn, flowEntryObj);
1289 }
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001290 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001291
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001292 for (FlowPath flowPath : flowPaths) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001293 //
1294 // Delete the flow entries from the switches
1295 //
1296 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
1297 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001298 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1299 if (mySwitch == null) {
1300 // Not my switch
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001301 installRemoteFlowEntry(flowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001302 } else {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001303 installFlowEntry(mySwitch, flowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001304 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001305 }
1306
1307 //
1308 // Calculate the new shortest path and install it in the
1309 // Network MAP.
1310 //
1311 FlowPath addedFlowPath = addAndMaintainShortestPathFlow(flowPath);
1312 if (addedFlowPath == null) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001313 log.error("Cannot add Shortest Path Flow from {} to {}: path not found?",
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001314 flowPath.dataPath().srcPort().toString(),
1315 flowPath.dataPath().dstPort().toString());
1316 continue;
1317 }
1318
1319 //
1320 // Add the flow entries to the switches
1321 //
1322 for (FlowEntry flowEntry : addedFlowPath.dataPath().flowEntries()) {
1323 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001324 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1325 if (mySwitch == null) {
1326 // Not my switch
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001327 installRemoteFlowEntry(addedFlowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001328 continue;
1329 }
1330
1331 IFlowEntry flowEntryObj =
1332 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId());
1333 if (flowEntryObj == null) {
1334 //
1335 // TODO: Remove the "new Object[] wrapper in the statement
1336 // below after the SLF4J logger is upgraded to
1337 // Version 1.7.5
1338 //
1339 log.error("Cannot add Flow Entry to switch {} for Path Flow from {} to {} : Flow Entry not in the Network MAP",
1340 new Object[] {
1341 flowEntry.dpid(),
1342 flowPath.dataPath().srcPort(),
1343 flowPath.dataPath().dstPort()
1344 });
1345 continue;
1346 }
1347 // Update the Flow Entry Switch State in the Network MAP
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001348 if (installFlowEntry(mySwitch, addedFlowPath, flowEntry)) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001349 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1350 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001351 }
1352 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001353 }
1354
1355 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001356 * Install a Flow Entry on a switch.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001357 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001358 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001359 * @param flowObj the flow path object for the flow entry to install.
1360 * @param flowEntryObj the flow entry object to install.
1361 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001362 */
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001363 public boolean installFlowEntry(IOFSwitch mySwitch, IFlowPath flowObj,
1364 IFlowEntry flowEntryObj) {
1365 FlowEntryId flowEntryId =
1366 new FlowEntryId(flowEntryObj.getFlowEntryId());
1367 String userState = flowEntryObj.getUserState();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001368
1369 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001370 // Create the Open Flow Flow Modification Entry to push
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001371 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001372 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1373 .getMessage(OFType.FLOW_MOD);
1374 long cookie = flowEntryId.value();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001375
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001376 short flowModCommand = OFFlowMod.OFPFC_ADD;
1377 if (userState.equals("FE_USER_ADD")) {
1378 flowModCommand = OFFlowMod.OFPFC_ADD;
1379 } else if (userState.equals("FE_USER_MODIFY")) {
1380 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1381 } else if (userState.equals("FE_USER_DELETE")) {
1382 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1383 } else {
1384 // Unknown user state. Ignore the entry
1385 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1386 flowEntryId.toString(), userState);
1387 return false;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001388 }
1389
1390 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001391 // Fetch the match conditions.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001392 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001393 // NOTE: The Flow matching conditions common for all Flow Entries are
1394 // used ONLY if a Flow Entry does NOT have the corresponding matching
1395 // condition set.
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001396 //
1397 OFMatch match = new OFMatch();
1398 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001399
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001400 // Match the Incoming Port
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001401 Short matchInPort = flowEntryObj.getMatchInPort();
1402 if (matchInPort != null) {
1403 match.setInputPort(matchInPort);
1404 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1405 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001406
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001407 // Match the Ethernet Frame Type
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001408 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1409 if (matchEthernetFrameType == null)
1410 matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1411 if (matchEthernetFrameType != null) {
1412 match.setDataLayerType(matchEthernetFrameType);
1413 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1414 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001415
1416 // Match the Source IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001417 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1418 if (matchSrcIPv4Net == null)
1419 matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1420 if (matchSrcIPv4Net != null) {
1421 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
1422 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001423
1424 // Natch the Destination IPv4 Network prefix
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001425 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1426 if (matchDstIPv4Net == null)
1427 matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1428 if (matchDstIPv4Net != null) {
1429 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
1430 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001431
1432 // Match the Source MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001433 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1434 if (matchSrcMac == null)
1435 matchSrcMac = flowObj.getMatchSrcMac();
1436 if (matchSrcMac != null) {
1437 match.setDataLayerSource(matchSrcMac);
1438 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1439 }
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001440
1441 // Match the Destination MAC address
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001442 String matchDstMac = flowEntryObj.getMatchDstMac();
1443 if (matchDstMac == null)
1444 matchDstMac = flowObj.getMatchDstMac();
1445 if (matchDstMac != null) {
1446 match.setDataLayerDestination(matchDstMac);
1447 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1448 }
1449
1450 //
1451 // Fetch the actions
1452 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001453 // TODO: For now we support only the "OUTPUT" actions.
1454 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001455 List<OFAction> actions = new ArrayList<OFAction>();
1456 Short actionOutputPort = flowEntryObj.getActionOutput();
1457 if (actionOutputPort != null) {
1458 OFActionOutput action = new OFActionOutput();
1459 // XXX: The max length is hard-coded for now
1460 action.setMaxLength((short)0xffff);
1461 action.setPort(actionOutputPort);
1462 actions.add(action);
1463 }
1464
1465 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1466 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1467 .setPriority(PRIORITY_DEFAULT)
1468 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1469 .setCookie(cookie)
1470 .setCommand(flowModCommand)
1471 .setMatch(match)
1472 .setActions(actions)
1473 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1474 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1475 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1476 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1477 if (actionOutputPort != null)
1478 fm.setOutPort(actionOutputPort);
1479 }
1480
1481 //
1482 // TODO: Set the following flag
1483 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1484 // See method ForwardingBase::pushRoute()
1485 //
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001486
1487 //
1488 // Write the message to the switch
1489 //
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001490 try {
1491 messageDamper.write(mySwitch, fm, null);
1492 mySwitch.flush();
1493 //
1494 // TODO: We should use the OpenFlow Barrier mechanism
1495 // to check for errors, and update the SwitchState
1496 // for a flow entry after the Barrier message is
1497 // is received.
1498 //
1499 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1500 } catch (IOException e) {
1501 log.error("Failure writing flow mod from network map", e);
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001502 return false;
Pavlin Radoslavovec8e2e62013-04-04 18:18:29 -07001503 }
1504
1505 return true;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001506 }
1507
1508 /**
1509 * Install a Flow Entry on a switch.
1510 *
1511 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001512 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001513 * @param flowEntry the flow entry to install.
1514 * @return true on success, otherwise false.
1515 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001516 public boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1517 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001518 //
1519 // Create the OpenFlow Flow Modification Entry to push
1520 //
1521 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1522 .getMessage(OFType.FLOW_MOD);
1523 long cookie = flowEntry.flowEntryId().value();
1524
1525 short flowModCommand = OFFlowMod.OFPFC_ADD;
1526 if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
1527 flowModCommand = OFFlowMod.OFPFC_ADD;
1528 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
1529 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1530 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
1531 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1532 } else {
1533 // Unknown user state. Ignore the entry
1534 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1535 flowEntry.flowEntryId().toString(),
1536 flowEntry.flowEntryUserState());
1537 return false;
1538 }
1539
1540 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001541 // Fetch the match conditions.
1542 //
1543 // NOTE: The Flow matching conditions common for all Flow Entries are
1544 // used ONLY if a Flow Entry does NOT have the corresponding matching
1545 // condition set.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001546 //
1547 OFMatch match = new OFMatch();
1548 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001549 FlowEntryMatch flowPathMatch = flowPath.flowEntryMatch();
1550 FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
1551
1552 // Match the Incoming Port
1553 Port matchInPort = flowEntryMatch.inPort();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001554 if (matchInPort != null) {
1555 match.setInputPort(matchInPort.value());
1556 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1557 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001558
1559 // Match the Ethernet Frame Type
1560 Short matchEthernetFrameType = flowEntryMatch.ethernetFrameType();
1561 if ((matchEthernetFrameType == null) && (flowPathMatch != null)) {
1562 matchEthernetFrameType = flowPathMatch.ethernetFrameType();
1563 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001564 if (matchEthernetFrameType != null) {
1565 match.setDataLayerType(matchEthernetFrameType);
1566 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1567 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001568
1569 // Match the Source IPv4 Network prefix
1570 IPv4Net matchSrcIPv4Net = flowEntryMatch.srcIPv4Net();
1571 if ((matchSrcIPv4Net == null) && (flowPathMatch != null)) {
1572 matchSrcIPv4Net = flowPathMatch.srcIPv4Net();
1573 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001574 if (matchSrcIPv4Net != null) {
1575 match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
1576 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001577
1578 // Natch the Destination IPv4 Network prefix
1579 IPv4Net matchDstIPv4Net = flowEntryMatch.dstIPv4Net();
1580 if ((matchDstIPv4Net == null) && (flowPathMatch != null)) {
1581 matchDstIPv4Net = flowPathMatch.dstIPv4Net();
1582 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001583 if (matchDstIPv4Net != null) {
1584 match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
1585 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001586
1587 // Match the Source MAC address
1588 MACAddress matchSrcMac = flowEntryMatch.srcMac();
1589 if ((matchSrcMac == null) && (flowPathMatch != null)) {
1590 matchSrcMac = flowPathMatch.srcMac();
1591 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001592 if (matchSrcMac != null) {
1593 match.setDataLayerSource(matchSrcMac.toString());
1594 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1595 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001596
1597 // Match the Destination MAC address
1598 MACAddress matchDstMac = flowEntryMatch.dstMac();
1599 if ((matchDstMac == null) && (flowPathMatch != null)) {
1600 matchDstMac = flowPathMatch.dstMac();
1601 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001602 if (matchDstMac != null) {
1603 match.setDataLayerDestination(matchDstMac.toString());
1604 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1605 }
1606
1607 //
1608 // Fetch the actions
1609 //
1610 // TODO: For now we support only the "OUTPUT" actions.
1611 //
1612 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1613 List<OFAction> actions = new ArrayList<OFAction>();
1614 ArrayList<FlowEntryAction> flowEntryActions =
1615 flowEntry.flowEntryActions();
1616 for (FlowEntryAction flowEntryAction : flowEntryActions) {
1617 FlowEntryAction.ActionOutput actionOutput =
1618 flowEntryAction.actionOutput();
1619 if (actionOutput != null) {
1620 short actionOutputPort = actionOutput.port().value();
1621 OFActionOutput action = new OFActionOutput();
1622 // XXX: The max length is hard-coded for now
1623 action.setMaxLength((short)0xffff);
1624 action.setPort(actionOutputPort);
1625 actions.add(action);
1626 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1627 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1628 fm.setOutPort(actionOutputPort);
1629 }
1630 }
1631 }
1632
1633 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1634 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1635 .setPriority(PRIORITY_DEFAULT)
1636 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1637 .setCookie(cookie)
1638 .setCommand(flowModCommand)
1639 .setMatch(match)
1640 .setActions(actions)
1641 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1642
1643 //
1644 // TODO: Set the following flag
1645 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1646 // See method ForwardingBase::pushRoute()
1647 //
1648
1649 //
1650 // Write the message to the switch
1651 //
1652 try {
1653 messageDamper.write(mySwitch, fm, null);
1654 mySwitch.flush();
Pavlin Radoslavov44a3dcd2013-04-04 18:42:56 -07001655 //
1656 // TODO: We should use the OpenFlow Barrier mechanism
1657 // to check for errors, and update the SwitchState
1658 // for a flow entry after the Barrier message is
1659 // is received.
1660 //
1661 // TODO: The FlowEntry Object in Titan should be set
1662 // to FE_SWITCH_UPDATED.
1663 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001664 } catch (IOException e) {
1665 log.error("Failure writing flow mod from network map", e);
1666 return false;
1667 }
1668 return true;
1669 }
1670
1671 /**
1672 * Remove a Flow Entry from a switch.
1673 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001674 * @param mySwitch the switch to remove the Flow Entry from.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001675 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001676 * @param flowEntry the flow entry to remove.
1677 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001678 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001679 public boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1680 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001681 //
1682 // The installFlowEntry() method implements both installation
1683 // and removal of flow entries.
1684 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001685 return (installFlowEntry(mySwitch, flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001686 }
1687
1688 /**
1689 * Install a Flow Entry on a remote controller.
1690 *
1691 * TODO: We need it now: Jono
1692 * - For now it will make a REST call to the remote controller.
1693 * - Internally, it needs to know the name of the remote controller.
1694 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001695 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001696 * @param flowEntry the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001697 * @return true on success, otherwise false.
1698 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001699 public boolean installRemoteFlowEntry(FlowPath flowPath,
1700 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001701 // TODO: We need it now: Jono
1702 // - For now it will make a REST call to the remote controller.
1703 // - Internally, it needs to know the name of the remote controller.
1704 return true;
1705 }
1706
1707 /**
1708 * Remove a flow entry on a remote controller.
1709 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001710 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001711 * @param flowEntry the flow entry to remove.
1712 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001713 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001714 public boolean removeRemoteFlowEntry(FlowPath flowPath,
1715 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001716 //
1717 // The installRemoteFlowEntry() method implements both installation
1718 // and removal of flow entries.
1719 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001720 return (installRemoteFlowEntry(flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001721 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001722}