blob: 83edc4955b22be355069cec86322752338f5abfc [file] [log] [blame]
Brian O'Connor8c166a72013-11-14 18:41:48 -08001package net.onrc.onos.ofcontroller.flowprogrammer;
Brian O'Connora8e49802013-10-30 20:49:59 -07002
3import java.io.IOException;
4import java.util.ArrayList;
Brian O'Connora8e49802013-10-30 20:49:59 -07005import java.util.HashMap;
6import java.util.HashSet;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
Naoki Shiota2bdda572013-12-09 15:05:21 -080010import java.util.concurrent.Callable;
Brian O'Connora8e49802013-10-30 20:49:59 -070011import java.util.concurrent.ExecutionException;
Brian O'Connora8e49802013-10-30 20:49:59 -070012import java.util.concurrent.Future;
Naoki Shiota2bdda572013-12-09 15:05:21 -080013import java.util.concurrent.FutureTask;
Brian O'Connora8e49802013-10-30 20:49:59 -070014
Brian O'Connor0d6ba512013-11-05 15:17:44 -080015import org.openflow.protocol.OFFlowMod;
Brian O'Connora8e49802013-10-30 20:49:59 -070016import org.openflow.protocol.OFMatch;
Brian O'Connor0d6ba512013-11-05 15:17:44 -080017import org.openflow.protocol.OFPort;
Brian O'Connora8e49802013-10-30 20:49:59 -070018import org.openflow.protocol.OFStatisticsRequest;
19import org.openflow.protocol.statistics.OFFlowStatisticsReply;
20import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
21import org.openflow.protocol.statistics.OFStatistics;
22import org.openflow.protocol.statistics.OFStatisticsType;
23import org.slf4j.Logger;
24import org.slf4j.LoggerFactory;
25
Brian O'Connora8e49802013-10-30 20:49:59 -070026import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connora8e49802013-10-30 20:49:59 -070027import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
28import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070029import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService.MsgPriority;
Brian O'Connora8e49802013-10-30 20:49:59 -070030import net.onrc.onos.ofcontroller.util.Dpid;
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -080031import net.onrc.onos.ofcontroller.util.FlowEntry;
Brian O'Connora8e49802013-10-30 20:49:59 -070032import net.onrc.onos.ofcontroller.util.FlowEntryId;
33
Naoki Shiotae3199732013-11-25 16:14:43 -080034/**
Naoki Shiotab485d412013-11-26 12:04:19 -080035 * FlowSynchronizer is an implementation of FlowSyncService.
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -080036 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
37 * tables from switches and compare them with GraphDB to drop unnecessary
38 * flows and/or to install missing flows.
Naoki Shiotae3199732013-11-25 16:14:43 -080039 * @author Brian
40 *
41 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080042public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070043
Brian O'Connorea1efbe2013-11-25 22:57:43 -080044 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080045
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070046 // TODO: fix after FlowSynchronizer is refactored
47 // private DBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080048 protected IFlowPusherService pusher;
Naoki Shiota2bdda572013-12-09 15:05:21 -080049 private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080050
51 public FlowSynchronizer() {
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070052 // TODO: fix after FlowSynchronizer is refactored
53 // dbHandler = GraphDBManager.getDBOperation();
Naoki Shiota2bdda572013-12-09 15:05:21 -080054 switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>();
Brian O'Connore46492e2013-11-14 21:11:50 -080055 }
56
Brian O'Connorea1efbe2013-11-25 22:57:43 -080057 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -080058 public Future<SyncResult> synchronize(IOFSwitch sw) {
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080059 Synchronizer sync = new Synchronizer(sw);
Naoki Shiota2bdda572013-12-09 15:05:21 -080060 FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync);
61 switchThreads.put(sw, task);
62 task.run();
63 return task;
Brian O'Connore46492e2013-11-14 21:11:50 -080064 }
Brian O'Connorea1efbe2013-11-25 22:57:43 -080065
Brian O'Connore46492e2013-11-14 21:11:50 -080066 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080067 public void interrupt(IOFSwitch sw) {
Naoki Shiota2bdda572013-12-09 15:05:21 -080068 FutureTask<SyncResult> t = switchThreads.remove(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080069 if(t != null) {
Naoki Shiota2bdda572013-12-09 15:05:21 -080070 t.cancel(true);
Brian O'Connorea1efbe2013-11-25 22:57:43 -080071 }
Brian O'Connore46492e2013-11-14 21:11:50 -080072 }
73
Naoki Shiotab485d412013-11-26 12:04:19 -080074 /**
75 * Initialize Synchronizer.
76 * @param pusherService FlowPusherService used for sending messages.
77 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080078 public void init(IFlowPusherService pusherService) {
79 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080080 }
81
Naoki Shiotae3199732013-11-25 16:14:43 -080082 /**
83 * Synchronizer represents main thread of synchronization.
84 * @author Brian
85 *
86 */
Naoki Shiota2bdda572013-12-09 15:05:21 -080087 protected class Synchronizer implements Callable<SyncResult> {
Brian O'Connora8e49802013-10-30 20:49:59 -070088 IOFSwitch sw;
89 ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080090
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080091 public Synchronizer(IOFSwitch sw) {
Brian O'Connora8e49802013-10-30 20:49:59 -070092 this.sw = sw;
93 Dpid dpid = new Dpid(sw.getId());
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070094 // TODO: fix after FlowSynchronizer is refactored
95 // this.swObj = dbHandler.searchSwitch(dpid.toString());
Brian O'Connora8e49802013-10-30 20:49:59 -070096 }
Brian O'Connore46492e2013-11-14 21:11:50 -080097
Brian O'Connor321a5d32013-12-09 18:11:35 -080098 double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime;
Brian O'Connora8e49802013-10-30 20:49:59 -070099 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -0800100 public SyncResult call() {
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700101 pusher.suspend(sw);
Naoki Shiota277dbd22014-03-20 19:06:48 -0700102 try {
103 long start = System.nanoTime();
104 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
105 long step1 = System.nanoTime();
106 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
107 if (switchEntries == null) {
108 log.debug("getFlowEntriesFromSwitch() failed");
109 return null;
110 }
111 long step2 = System.nanoTime();
112 SyncResult result = compare(graphEntries, switchEntries);
113 long step3 = System.nanoTime();
114 graphIDTime = (step1 - start);
115 switchTime = (step2 - step1);
116 compareTime = (step3 - step2);
117 totalTime = (step3 - start);
118 outputTime();
119
120 return result;
121 } finally {
122 pusher.resume(sw);
Naoki Shiotadf051d42014-01-20 16:12:41 -0800123 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700124 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800125
126 private void outputTime() {
127 double div = Math.pow(10, 6); //convert nanoseconds to ms
128 graphIDTime /= div;
129 switchTime /= div;
130 compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
131 graphEntryTime /= div;
132 extractTime /= div;
133 pushTime /= div;
Brian O'Connor8f7f8582013-12-11 15:48:07 -0800134 totalTime /= div;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800135 log.debug("Sync time (ms):{},{},{},{},{},{},{}"
136 , graphIDTime
137 , switchTime
138 , compareTime
139 , graphEntryTime
140 , extractTime
141 , pushTime
142 , totalTime);
Brian O'Connora8e49802013-10-30 20:49:59 -0700143 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800144
Naoki Shiotae3199732013-11-25 16:14:43 -0800145 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800146 * Compare flows entries in GraphDB and switch to pick up necessary
147 * messages.
Naoki Shiotae3199732013-11-25 16:14:43 -0800148 * After picking up, picked messages are added to FlowPusher.
149 * @param graphEntries Flow entries in GraphDB.
150 * @param switchEntries Flow entries in switch.
151 */
Naoki Shiota2bdda572013-12-09 15:05:21 -0800152 private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800153 int added = 0, removed = 0, skipped = 0;
154 for(FlowEntryWrapper entry : switchEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800155 if(graphEntries.contains(entry)) {
156 graphEntries.remove(entry);
157 skipped++;
158 }
159 else {
160 // remove flow entry from the switch
161 entry.removeFromSwitch(sw);
162 removed++;
163 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700164 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800165 for(FlowEntryWrapper entry : graphEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800166 // add flow entry to switch
167 entry.addToSwitch(sw);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800168 graphEntryTime += entry.dbTime;
169 extractTime += entry.extractTime;
170 pushTime += entry.pushTime;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800171 added++;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800172 }
173 log.debug("Flow entries added {}, " +
174 "Flow entries removed {}, " +
175 "Flow entries skipped {}"
176 , added
177 , removed
178 , skipped );
179
Naoki Shiota2bdda572013-12-09 15:05:21 -0800180 return new SyncResult(added, removed, skipped);
Brian O'Connora8e49802013-10-30 20:49:59 -0700181 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800182
Naoki Shiotae3199732013-11-25 16:14:43 -0800183 /**
184 * Read GraphDB to get FlowEntries associated with a switch.
185 * @return set of FlowEntries
186 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800187 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
188 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700189 for(IFlowEntry entry : swObj.getFlowEntries()) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800190 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
191 entries.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700192 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800193 return entries;
Brian O'Connora8e49802013-10-30 20:49:59 -0700194 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800195
Naoki Shiotae3199732013-11-25 16:14:43 -0800196 /**
197 * Read flow table from switch and derive FlowEntries from table.
198 * @return set of FlowEntries
199 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800200 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700201
202 int lengthU = 0;
203 OFMatch match = new OFMatch();
204 match.setWildcards(OFMatch.OFPFW_ALL);
205
206 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
207 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
208 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
209 stat.setMatch(match);
210 List<OFStatistics> stats = new ArrayList<OFStatistics>();
211 stats.add(stat);
212 lengthU += stat.getLength();
213
214 OFStatisticsRequest req = new OFStatisticsRequest();
215 req.setStatisticType(OFStatisticsType.FLOW);
216 req.setStatistics(stats);
217 lengthU += req.getLengthU();
218 req.setLengthU(lengthU);
219
220 List<OFStatistics> entries = null;
221 try {
222 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
223 entries = dfuture.get();
224 } catch (IOException e) {
225 // TODO Auto-generated catch block
226 e.printStackTrace();
Naoki Shiotadf051d42014-01-20 16:12:41 -0800227 return null;
Brian O'Connora8e49802013-10-30 20:49:59 -0700228 } catch (InterruptedException e) {
229 // TODO Auto-generated catch block
230 e.printStackTrace();
Naoki Shiotadf051d42014-01-20 16:12:41 -0800231 return null;
Brian O'Connora8e49802013-10-30 20:49:59 -0700232 } catch (ExecutionException e) {
233 // TODO Auto-generated catch block
234 e.printStackTrace();
Naoki Shiotadf051d42014-01-20 16:12:41 -0800235 return null;
Brian O'Connora8e49802013-10-30 20:49:59 -0700236 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800237
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800238 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700239 for(OFStatistics result : entries){
Brian O'Connora8e49802013-10-30 20:49:59 -0700240 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800241 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
242 results.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700243 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800244 return results;
Brian O'Connora8e49802013-10-30 20:49:59 -0700245 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800246
Brian O'Connora8e49802013-10-30 20:49:59 -0700247 }
248
Naoki Shiotae3199732013-11-25 16:14:43 -0800249 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800250 * FlowEntryWrapper represents abstract FlowEntry which is embodied
251 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800252 * @author Brian
253 *
254 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800255 class FlowEntryWrapper {
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800256 FlowEntryId flowEntryId;
257 IFlowEntry iFlowEntry;
258 OFFlowStatisticsReply statisticsReply;
259
Brian O'Connore46492e2013-11-14 21:11:50 -0800260
261 public FlowEntryWrapper(IFlowEntry entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800262 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800263 iFlowEntry = entry;
264 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700265
Brian O'Connore46492e2013-11-14 21:11:50 -0800266 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800267 flowEntryId = new FlowEntryId(entry.getCookie());
Brian O'Connore46492e2013-11-14 21:11:50 -0800268 statisticsReply = entry;
Brian O'Connore46492e2013-11-14 21:11:50 -0800269 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700270
Naoki Shiotae3199732013-11-25 16:14:43 -0800271 /**
272 * Install this FlowEntry to a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800273 * @param sw Switch to which flow will be installed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800274 */
Brian O'Connor321a5d32013-12-09 18:11:35 -0800275 double dbTime, extractTime, pushTime;
Brian O'Connore46492e2013-11-14 21:11:50 -0800276 public void addToSwitch(IOFSwitch sw) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800277 if (statisticsReply != null) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800278 log.error("Error adding existing flow entry {} to sw {}",
Brian O'Connore46492e2013-11-14 21:11:50 -0800279 statisticsReply.getCookie(), sw.getId());
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800280 return;
Brian O'Connore46492e2013-11-14 21:11:50 -0800281 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800282
Brian O'Connor321a5d32013-12-09 18:11:35 -0800283 double startDB = System.nanoTime();
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800284 // Get the Flow Entry state from the Network Graph
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800285 if (iFlowEntry == null) {
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800286 try {
Pavlin Radoslavov697f6982014-03-26 15:55:19 -0700287 // TODO: fix after FlowSynchronizer is refactored
288 // iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800289 } catch (Exception e) {
290 log.error("Error finding flow entry {} in Network Graph",
291 flowEntryId);
292 return;
293 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800294 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800295 dbTime = System.nanoTime() - startDB;
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800296
Pavlin Radoslavov39f0f2e2014-03-20 12:04:57 -0700297 //
298 // TODO: The old FlowDatabaseOperation class is gone, so the code
299 //
300 /*
Brian O'Connor321a5d32013-12-09 18:11:35 -0800301 double startExtract = System.nanoTime();
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -0800302 FlowEntry flowEntry =
303 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
304 if (flowEntry == null) {
305 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
306 flowEntryId, sw.getId());
307 return;
308 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800309 extractTime = System.nanoTime() - startExtract;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800310
Brian O'Connor321a5d32013-12-09 18:11:35 -0800311 double startPush = System.nanoTime();
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700312 pusher.pushFlowEntry(sw, flowEntry, MsgPriority.HIGH);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800313 pushTime = System.nanoTime() - startPush;
Pavlin Radoslavov39f0f2e2014-03-20 12:04:57 -0700314 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800315 }
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800316
Naoki Shiotae3199732013-11-25 16:14:43 -0800317 /**
318 * Remove this FlowEntry from a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800319 * @param sw Switch from which flow will be removed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800320 */
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800321 public void removeFromSwitch(IOFSwitch sw) {
322 if (statisticsReply == null) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800323 log.error("Error removing non-existent flow entry {} from sw {}",
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800324 flowEntryId, sw.getId());
325 return;
326 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700327
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800328 // Convert Statistics Reply to Flow Mod, then write it
329 OFFlowMod fm = new OFFlowMod();
330 fm.setCookie(statisticsReply.getCookie());
331 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
332 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
333 fm.setMatch(statisticsReply.getMatch());
334 fm.setPriority(statisticsReply.getPriority());
335 fm.setOutPort(OFPort.OFPP_NONE);
336
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700337 pusher.add(sw, fm, MsgPriority.HIGH);
Brian O'Connore46492e2013-11-14 21:11:50 -0800338 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700339
Brian O'Connore46492e2013-11-14 21:11:50 -0800340 /**
341 * Return the hash code of the Flow Entry ID
342 */
343 @Override
344 public int hashCode() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800345 return flowEntryId.hashCode();
Brian O'Connore46492e2013-11-14 21:11:50 -0800346 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700347
Brian O'Connore46492e2013-11-14 21:11:50 -0800348 /**
349 * Returns true of the object is another Flow Entry ID with
350 * the same value; otherwise, returns false.
351 *
352 * @param Object to compare
Naoki Shiotab485d412013-11-26 12:04:19 -0800353 * @return true if the object has the same Flow Entry ID.
Brian O'Connore46492e2013-11-14 21:11:50 -0800354 */
355 @Override
356 public boolean equals(Object obj){
Naoki Shiotadf051d42014-01-20 16:12:41 -0800357 if(obj != null && obj.getClass() == this.getClass()) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800358 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
359 // TODO: we need to actually compare the match + actions
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800360 return this.flowEntryId.equals(entry.flowEntryId);
Brian O'Connore46492e2013-11-14 21:11:50 -0800361 }
362 return false;
363 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800364
Brian O'Connore46492e2013-11-14 21:11:50 -0800365 @Override
366 public String toString() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800367 return flowEntryId.toString();
Brian O'Connore46492e2013-11-14 21:11:50 -0800368 }
369 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800370}