blob: c1571c83ce344ebc6bc85d4bed0181de28ade767 [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;
yoshied0a6eb2013-12-05 16:54:27 -080027import net.onrc.onos.graph.DBOperation;
28import net.onrc.onos.graph.GraphDBManager;
Brian O'Connora8e49802013-10-30 20:49:59 -070029import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
30import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -080031import net.onrc.onos.ofcontroller.flowmanager.FlowDatabaseOperation;
Naoki Shiota8df97bc2014-03-13 18:42:23 -070032import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService.MsgPriority;
Brian O'Connora8e49802013-10-30 20:49:59 -070033import net.onrc.onos.ofcontroller.util.Dpid;
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -080034import net.onrc.onos.ofcontroller.util.FlowEntry;
Brian O'Connora8e49802013-10-30 20:49:59 -070035import net.onrc.onos.ofcontroller.util.FlowEntryId;
36
Naoki Shiotae3199732013-11-25 16:14:43 -080037/**
Naoki Shiotab485d412013-11-26 12:04:19 -080038 * FlowSynchronizer is an implementation of FlowSyncService.
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -080039 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
40 * tables from switches and compare them with GraphDB to drop unnecessary
41 * flows and/or to install missing flows.
Naoki Shiotae3199732013-11-25 16:14:43 -080042 * @author Brian
43 *
44 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080045public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070046
Brian O'Connorea1efbe2013-11-25 22:57:43 -080047 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080048
yoshied0a6eb2013-12-05 16:54:27 -080049 private DBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080050 protected IFlowPusherService pusher;
Naoki Shiota2bdda572013-12-09 15:05:21 -080051 private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080052
53 public FlowSynchronizer() {
Yoshi Muroi5804ce92014-02-08 03:58:04 -080054 dbHandler = GraphDBManager.getDBOperation();
Naoki Shiota2bdda572013-12-09 15:05:21 -080055 switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>();
Brian O'Connore46492e2013-11-14 21:11:50 -080056 }
57
Brian O'Connorea1efbe2013-11-25 22:57:43 -080058 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -080059 public Future<SyncResult> synchronize(IOFSwitch sw) {
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080060 Synchronizer sync = new Synchronizer(sw);
Naoki Shiota2bdda572013-12-09 15:05:21 -080061 FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync);
62 switchThreads.put(sw, task);
63 task.run();
64 return task;
Brian O'Connore46492e2013-11-14 21:11:50 -080065 }
Brian O'Connorea1efbe2013-11-25 22:57:43 -080066
Brian O'Connore46492e2013-11-14 21:11:50 -080067 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080068 public void interrupt(IOFSwitch sw) {
Naoki Shiota2bdda572013-12-09 15:05:21 -080069 FutureTask<SyncResult> t = switchThreads.remove(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080070 if(t != null) {
Naoki Shiota2bdda572013-12-09 15:05:21 -080071 t.cancel(true);
Brian O'Connorea1efbe2013-11-25 22:57:43 -080072 }
Brian O'Connore46492e2013-11-14 21:11:50 -080073 }
74
Naoki Shiotab485d412013-11-26 12:04:19 -080075 /**
76 * Initialize Synchronizer.
77 * @param pusherService FlowPusherService used for sending messages.
78 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080079 public void init(IFlowPusherService pusherService) {
80 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080081 }
82
Naoki Shiotae3199732013-11-25 16:14:43 -080083 /**
84 * Synchronizer represents main thread of synchronization.
85 * @author Brian
86 *
87 */
Naoki Shiota2bdda572013-12-09 15:05:21 -080088 protected class Synchronizer implements Callable<SyncResult> {
Brian O'Connora8e49802013-10-30 20:49:59 -070089 IOFSwitch sw;
90 ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080091
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080092 public Synchronizer(IOFSwitch sw) {
Brian O'Connora8e49802013-10-30 20:49:59 -070093 this.sw = sw;
94 Dpid dpid = new Dpid(sw.getId());
95 this.swObj = dbHandler.searchSwitch(dpid.toString());
96 }
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);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800102 long start = System.nanoTime();
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800103 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
Brian O'Connor321a5d32013-12-09 18:11:35 -0800104 long step1 = System.nanoTime();
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800105 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
Naoki Shiotadf051d42014-01-20 16:12:41 -0800106 if (switchEntries == null) {
107 log.debug("getFlowEntriesFromSwitch() failed");
108 return null;
109 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800110 long step2 = System.nanoTime();
Naoki Shiota2bdda572013-12-09 15:05:21 -0800111 SyncResult result = compare(graphEntries, switchEntries);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800112 long step3 = System.nanoTime();
113 graphIDTime = (step1 - start);
114 switchTime = (step2 - step1);
115 compareTime = (step3 - step2);
116 totalTime = (step3 - start);
117 outputTime();
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700118 pusher.resume(sw);
Naoki Shiota2bdda572013-12-09 15:05:21 -0800119
120 return result;
Brian O'Connora8e49802013-10-30 20:49:59 -0700121 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800122
123 private void outputTime() {
124 double div = Math.pow(10, 6); //convert nanoseconds to ms
125 graphIDTime /= div;
126 switchTime /= div;
127 compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
128 graphEntryTime /= div;
129 extractTime /= div;
130 pushTime /= div;
Brian O'Connor8f7f8582013-12-11 15:48:07 -0800131 totalTime /= div;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800132 log.debug("Sync time (ms):{},{},{},{},{},{},{}"
133 , graphIDTime
134 , switchTime
135 , compareTime
136 , graphEntryTime
137 , extractTime
138 , pushTime
139 , totalTime);
Brian O'Connora8e49802013-10-30 20:49:59 -0700140 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800141
Naoki Shiotae3199732013-11-25 16:14:43 -0800142 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800143 * Compare flows entries in GraphDB and switch to pick up necessary
144 * messages.
Naoki Shiotae3199732013-11-25 16:14:43 -0800145 * After picking up, picked messages are added to FlowPusher.
146 * @param graphEntries Flow entries in GraphDB.
147 * @param switchEntries Flow entries in switch.
148 */
Naoki Shiota2bdda572013-12-09 15:05:21 -0800149 private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800150 int added = 0, removed = 0, skipped = 0;
151 for(FlowEntryWrapper entry : switchEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800152 if(graphEntries.contains(entry)) {
153 graphEntries.remove(entry);
154 skipped++;
155 }
156 else {
157 // remove flow entry from the switch
158 entry.removeFromSwitch(sw);
159 removed++;
160 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700161 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800162 for(FlowEntryWrapper entry : graphEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800163 // add flow entry to switch
164 entry.addToSwitch(sw);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800165 graphEntryTime += entry.dbTime;
166 extractTime += entry.extractTime;
167 pushTime += entry.pushTime;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800168 added++;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800169 }
170 log.debug("Flow entries added {}, " +
171 "Flow entries removed {}, " +
172 "Flow entries skipped {}"
173 , added
174 , removed
175 , skipped );
176
Naoki Shiota2bdda572013-12-09 15:05:21 -0800177 return new SyncResult(added, removed, skipped);
Brian O'Connora8e49802013-10-30 20:49:59 -0700178 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800179
Naoki Shiotae3199732013-11-25 16:14:43 -0800180 /**
181 * Read GraphDB to get FlowEntries associated with a switch.
182 * @return set of FlowEntries
183 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800184 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
185 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700186 for(IFlowEntry entry : swObj.getFlowEntries()) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800187 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
188 entries.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700189 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800190 return entries;
Brian O'Connora8e49802013-10-30 20:49:59 -0700191 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800192
Naoki Shiotae3199732013-11-25 16:14:43 -0800193 /**
194 * Read flow table from switch and derive FlowEntries from table.
195 * @return set of FlowEntries
196 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800197 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700198
199 int lengthU = 0;
200 OFMatch match = new OFMatch();
201 match.setWildcards(OFMatch.OFPFW_ALL);
202
203 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
204 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
205 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
206 stat.setMatch(match);
207 List<OFStatistics> stats = new ArrayList<OFStatistics>();
208 stats.add(stat);
209 lengthU += stat.getLength();
210
211 OFStatisticsRequest req = new OFStatisticsRequest();
212 req.setStatisticType(OFStatisticsType.FLOW);
213 req.setStatistics(stats);
214 lengthU += req.getLengthU();
215 req.setLengthU(lengthU);
216
217 List<OFStatistics> entries = null;
218 try {
219 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
220 entries = dfuture.get();
221 } catch (IOException e) {
222 // TODO Auto-generated catch block
223 e.printStackTrace();
Naoki Shiotadf051d42014-01-20 16:12:41 -0800224 return null;
Brian O'Connora8e49802013-10-30 20:49:59 -0700225 } catch (InterruptedException e) {
226 // TODO Auto-generated catch block
227 e.printStackTrace();
Naoki Shiotadf051d42014-01-20 16:12:41 -0800228 return null;
Brian O'Connora8e49802013-10-30 20:49:59 -0700229 } catch (ExecutionException e) {
230 // TODO Auto-generated catch block
231 e.printStackTrace();
Naoki Shiotadf051d42014-01-20 16:12:41 -0800232 return null;
Brian O'Connora8e49802013-10-30 20:49:59 -0700233 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800234
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800235 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700236 for(OFStatistics result : entries){
Brian O'Connora8e49802013-10-30 20:49:59 -0700237 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800238 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
239 results.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700240 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800241 return results;
Brian O'Connora8e49802013-10-30 20:49:59 -0700242 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800243
Brian O'Connora8e49802013-10-30 20:49:59 -0700244 }
245
Naoki Shiotae3199732013-11-25 16:14:43 -0800246 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800247 * FlowEntryWrapper represents abstract FlowEntry which is embodied
248 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800249 * @author Brian
250 *
251 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800252 class FlowEntryWrapper {
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800253 FlowEntryId flowEntryId;
254 IFlowEntry iFlowEntry;
255 OFFlowStatisticsReply statisticsReply;
256
Brian O'Connore46492e2013-11-14 21:11:50 -0800257
258 public FlowEntryWrapper(IFlowEntry entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800259 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800260 iFlowEntry = entry;
261 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700262
Brian O'Connore46492e2013-11-14 21:11:50 -0800263 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800264 flowEntryId = new FlowEntryId(entry.getCookie());
Brian O'Connore46492e2013-11-14 21:11:50 -0800265 statisticsReply = entry;
Brian O'Connore46492e2013-11-14 21:11:50 -0800266 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700267
Naoki Shiotae3199732013-11-25 16:14:43 -0800268 /**
269 * Install this FlowEntry to a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800270 * @param sw Switch to which flow will be installed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800271 */
Brian O'Connor321a5d32013-12-09 18:11:35 -0800272 double dbTime, extractTime, pushTime;
Brian O'Connore46492e2013-11-14 21:11:50 -0800273 public void addToSwitch(IOFSwitch sw) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800274 if (statisticsReply != null) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800275 log.error("Error adding existing flow entry {} to sw {}",
Brian O'Connore46492e2013-11-14 21:11:50 -0800276 statisticsReply.getCookie(), sw.getId());
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800277 return;
Brian O'Connore46492e2013-11-14 21:11:50 -0800278 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800279
Brian O'Connor321a5d32013-12-09 18:11:35 -0800280 double startDB = System.nanoTime();
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800281 // Get the Flow Entry state from the Network Graph
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800282 if (iFlowEntry == null) {
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800283 try {
284 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
285 } catch (Exception e) {
286 log.error("Error finding flow entry {} in Network Graph",
287 flowEntryId);
288 return;
289 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800290 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800291 dbTime = System.nanoTime() - startDB;
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800292
Brian O'Connor321a5d32013-12-09 18:11:35 -0800293 double startExtract = System.nanoTime();
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -0800294 FlowEntry flowEntry =
295 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
296 if (flowEntry == null) {
297 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
298 flowEntryId, sw.getId());
299 return;
300 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800301 extractTime = System.nanoTime() - startExtract;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800302
Brian O'Connor321a5d32013-12-09 18:11:35 -0800303 double startPush = System.nanoTime();
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700304 pusher.pushFlowEntry(sw, flowEntry, MsgPriority.HIGH);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800305 pushTime = System.nanoTime() - startPush;
Brian O'Connore46492e2013-11-14 21:11:50 -0800306 }
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800307
Naoki Shiotae3199732013-11-25 16:14:43 -0800308 /**
309 * Remove this FlowEntry from a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800310 * @param sw Switch from which flow will be removed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800311 */
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800312 public void removeFromSwitch(IOFSwitch sw) {
313 if (statisticsReply == null) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800314 log.error("Error removing non-existent flow entry {} from sw {}",
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800315 flowEntryId, sw.getId());
316 return;
317 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700318
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800319 // Convert Statistics Reply to Flow Mod, then write it
320 OFFlowMod fm = new OFFlowMod();
321 fm.setCookie(statisticsReply.getCookie());
322 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
323 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
324 fm.setMatch(statisticsReply.getMatch());
325 fm.setPriority(statisticsReply.getPriority());
326 fm.setOutPort(OFPort.OFPP_NONE);
327
Naoki Shiota8df97bc2014-03-13 18:42:23 -0700328 pusher.add(sw, fm, MsgPriority.HIGH);
Brian O'Connore46492e2013-11-14 21:11:50 -0800329 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700330
Brian O'Connore46492e2013-11-14 21:11:50 -0800331 /**
332 * Return the hash code of the Flow Entry ID
333 */
334 @Override
335 public int hashCode() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800336 return flowEntryId.hashCode();
Brian O'Connore46492e2013-11-14 21:11:50 -0800337 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700338
Brian O'Connore46492e2013-11-14 21:11:50 -0800339 /**
340 * Returns true of the object is another Flow Entry ID with
341 * the same value; otherwise, returns false.
342 *
343 * @param Object to compare
Naoki Shiotab485d412013-11-26 12:04:19 -0800344 * @return true if the object has the same Flow Entry ID.
Brian O'Connore46492e2013-11-14 21:11:50 -0800345 */
346 @Override
347 public boolean equals(Object obj){
Naoki Shiotadf051d42014-01-20 16:12:41 -0800348 if(obj != null && obj.getClass() == this.getClass()) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800349 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
350 // TODO: we need to actually compare the match + actions
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800351 return this.flowEntryId.equals(entry.flowEntryId);
Brian O'Connore46492e2013-11-14 21:11:50 -0800352 }
353 return false;
354 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800355
Brian O'Connore46492e2013-11-14 21:11:50 -0800356 @Override
357 public String toString() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800358 return flowEntryId.toString();
Brian O'Connore46492e2013-11-14 21:11:50 -0800359 }
360 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800361}