blob: 005f983519a17b022a12a477a7dbc38bee7db816 [file] [log] [blame]
Jonathan Hart23701d12014-04-03 10:45:48 -07001package net.onrc.onos.core.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
Jonathan Harta99ec672014-04-03 11:30:34 -070015import net.floodlightcontroller.core.IOFSwitch;
16import net.onrc.onos.core.flowprogrammer.IFlowPusherService.MsgPriority;
Jonathan Harta99ec672014-04-03 11:30:34 -070017import net.onrc.onos.core.util.FlowEntryId;
18
Brian O'Connor0d6ba512013-11-05 15:17:44 -080019import org.openflow.protocol.OFFlowMod;
Brian O'Connora8e49802013-10-30 20:49:59 -070020import org.openflow.protocol.OFMatch;
Brian O'Connor0d6ba512013-11-05 15:17:44 -080021import org.openflow.protocol.OFPort;
Brian O'Connora8e49802013-10-30 20:49:59 -070022import org.openflow.protocol.OFStatisticsRequest;
23import org.openflow.protocol.statistics.OFFlowStatisticsReply;
24import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
25import org.openflow.protocol.statistics.OFStatistics;
26import org.openflow.protocol.statistics.OFStatisticsType;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
Naoki Shiotae3199732013-11-25 16:14:43 -080030/**
Naoki Shiotab485d412013-11-26 12:04:19 -080031 * FlowSynchronizer is an implementation of FlowSyncService.
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -080032 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
33 * tables from switches and compare them with GraphDB to drop unnecessary
34 * flows and/or to install missing flows.
Naoki Shiotae3199732013-11-25 16:14:43 -080035 *
Ray Milkey8e5170e2014-04-02 12:09:55 -070036 * @author Brian
Naoki Shiotae3199732013-11-25 16:14:43 -080037 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080038public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070039
Brian O'Connorea1efbe2013-11-25 22:57:43 -080040 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080041
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -070042 // TODO: fix when FlowSynchronizer is refactored
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070043 // private DBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080044 protected IFlowPusherService pusher;
Ray Milkey8e5170e2014-04-02 12:09:55 -070045 private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080046
47 public FlowSynchronizer() {
Ray Milkey8e5170e2014-04-02 12:09:55 -070048 // TODO: fix when FlowSynchronizer is refactored
49 // dbHandler = GraphDBManager.getDBOperation();
50 switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>();
Brian O'Connore46492e2013-11-14 21:11:50 -080051 }
52
Brian O'Connorea1efbe2013-11-25 22:57:43 -080053 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -080054 public Future<SyncResult> synchronize(IOFSwitch sw) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070055 Synchronizer sync = new Synchronizer(sw);
56 FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync);
57 switchThreads.put(sw, task);
58 task.run();
59 return task;
Brian O'Connore46492e2013-11-14 21:11:50 -080060 }
Ray Milkey8e5170e2014-04-02 12:09:55 -070061
Brian O'Connore46492e2013-11-14 21:11:50 -080062 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080063 public void interrupt(IOFSwitch sw) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070064 FutureTask<SyncResult> t = switchThreads.remove(sw);
65 if (t != null) {
66 t.cancel(true);
67 }
Brian O'Connore46492e2013-11-14 21:11:50 -080068 }
69
Naoki Shiotab485d412013-11-26 12:04:19 -080070 /**
71 * Initialize Synchronizer.
Ray Milkey8e5170e2014-04-02 12:09:55 -070072 *
Naoki Shiotab485d412013-11-26 12:04:19 -080073 * @param pusherService FlowPusherService used for sending messages.
74 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080075 public void init(IFlowPusherService pusherService) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070076 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080077 }
78
Naoki Shiotae3199732013-11-25 16:14:43 -080079 /**
80 * Synchronizer represents main thread of synchronization.
Naoki Shiotae3199732013-11-25 16:14:43 -080081 *
Ray Milkey8e5170e2014-04-02 12:09:55 -070082 * @author Brian
Naoki Shiotae3199732013-11-25 16:14:43 -080083 */
Ray Milkey8e5170e2014-04-02 12:09:55 -070084 protected class Synchronizer implements Callable<SyncResult> {
85 IOFSwitch sw;
86 // TODO: fix when FlowSynchronizer is refactored
87 // ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080088
Ray Milkey8e5170e2014-04-02 12:09:55 -070089 public Synchronizer(IOFSwitch sw) {
90 this.sw = sw;
Ray Milkey8e5170e2014-04-02 12:09:55 -070091 // TODO: fix when FlowSynchronizer is refactored
Pavlin Radoslavov3255ae52014-04-09 13:01:54 -070092 // Dpid dpid = new Dpid(sw.getId());
Ray Milkey8e5170e2014-04-02 12:09:55 -070093 // this.swObj = dbHandler.searchSwitch(dpid.toString());
94 }
Brian O'Connore46492e2013-11-14 21:11:50 -080095
Ray Milkey8e5170e2014-04-02 12:09:55 -070096 double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime;
Brian O'Connore46492e2013-11-14 21:11:50 -080097
Ray Milkey8e5170e2014-04-02 12:09:55 -070098 @Override
99 public SyncResult call() {
100 pusher.suspend(sw);
101 try {
102 long start = System.nanoTime();
103 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
104 long step1 = System.nanoTime();
105 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
106 if (switchEntries == null) {
107 log.debug("getFlowEntriesFromSwitch() failed");
108 return null;
109 }
110 long step2 = System.nanoTime();
111 SyncResult result = compare(graphEntries, switchEntries);
112 long step3 = System.nanoTime();
113 graphIDTime = (step1 - start);
114 switchTime = (step2 - step1);
115 compareTime = (step3 - step2);
116 totalTime = (step3 - start);
117 outputTime();
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800118
Ray Milkey8e5170e2014-04-02 12:09:55 -0700119 return result;
120 } finally {
121 pusher.resume(sw);
122 }
123 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800124
Ray Milkey8e5170e2014-04-02 12:09:55 -0700125 private void outputTime() {
126 double div = Math.pow(10, 6); //convert nanoseconds to ms
127 graphIDTime /= div;
128 switchTime /= div;
129 compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
130 graphEntryTime /= div;
131 extractTime /= div;
132 pushTime /= div;
133 totalTime /= div;
134 log.debug("Sync time (ms):{},{},{},{},{},{},{}"
135 , graphIDTime
136 , switchTime
137 , compareTime
138 , graphEntryTime
139 , extractTime
140 , pushTime
141 , totalTime);
142 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700143
Ray Milkey8e5170e2014-04-02 12:09:55 -0700144 /**
145 * Compare flows entries in GraphDB and switch to pick up necessary
146 * messages.
147 * After picking up, picked messages are added to FlowPusher.
148 *
149 * @param graphEntries Flow entries in GraphDB.
150 * @param switchEntries Flow entries in switch.
151 */
152 private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
153 int added = 0, removed = 0, skipped = 0;
154 for (FlowEntryWrapper entry : switchEntries) {
155 if (graphEntries.contains(entry)) {
156 graphEntries.remove(entry);
157 skipped++;
158 } else {
159 // remove flow entry from the switch
160 entry.removeFromSwitch(sw);
161 removed++;
162 }
163 }
164 for (FlowEntryWrapper entry : graphEntries) {
165 // add flow entry to switch
166 entry.addToSwitch(sw);
167 graphEntryTime += entry.dbTime;
168 extractTime += entry.extractTime;
169 pushTime += entry.pushTime;
170 added++;
171 }
172 log.debug("Flow entries added {}, " +
173 "Flow entries removed {}, " +
174 "Flow entries skipped {}"
175 , added
176 , removed
177 , skipped);
Brian O'Connore46492e2013-11-14 21:11:50 -0800178
Ray Milkey8e5170e2014-04-02 12:09:55 -0700179 return new SyncResult(added, removed, skipped);
180 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700181
Ray Milkey8e5170e2014-04-02 12:09:55 -0700182 /**
183 * Read GraphDB to get FlowEntries associated with a switch.
184 *
185 * @return set of FlowEntries
186 */
187 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
188 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700189
Ray Milkey8e5170e2014-04-02 12:09:55 -0700190 // TODO: fix when FlowSynchronizer is refactored
191 /*
192 for(IFlowEntry entry : swObj.getFlowEntries()) {
193 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
194 entries.add(fe);
195 }
196 */
197 return entries;
198 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700199
Ray Milkey8e5170e2014-04-02 12:09:55 -0700200 /**
201 * Read flow table from switch and derive FlowEntries from table.
202 *
203 * @return set of FlowEntries
204 */
205 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700206
Ray Milkey8e5170e2014-04-02 12:09:55 -0700207 int lengthU = 0;
208 OFMatch match = new OFMatch();
209 match.setWildcards(OFMatch.OFPFW_ALL);
Brian O'Connore46492e2013-11-14 21:11:50 -0800210
Ray Milkey8e5170e2014-04-02 12:09:55 -0700211 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
212 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
213 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
214 stat.setMatch(match);
215 List<OFStatistics> stats = new ArrayList<OFStatistics>();
216 stats.add(stat);
217 lengthU += stat.getLength();
218
219 OFStatisticsRequest req = new OFStatisticsRequest();
220 req.setStatisticType(OFStatisticsType.FLOW);
221 req.setStatistics(stats);
222 lengthU += req.getLengthU();
223 req.setLengthU(lengthU);
224
225 List<OFStatistics> entries = null;
226 try {
227 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
228 entries = dfuture.get();
229 } catch (IOException e) {
230 // TODO Auto-generated catch block
231 e.printStackTrace();
232 return null;
233 } catch (InterruptedException e) {
234 // TODO Auto-generated catch block
235 e.printStackTrace();
236 return null;
237 } catch (ExecutionException e) {
238 // TODO Auto-generated catch block
239 e.printStackTrace();
240 return null;
241 }
242
243 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
244 for (OFStatistics result : entries) {
245 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
246 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
247 results.add(fe);
248 }
249 return results;
250 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800251
Brian O'Connora8e49802013-10-30 20:49:59 -0700252 }
253
Naoki Shiotae3199732013-11-25 16:14:43 -0800254 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800255 * FlowEntryWrapper represents abstract FlowEntry which is embodied
256 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800257 *
Ray Milkey8e5170e2014-04-02 12:09:55 -0700258 * @author Brian
Naoki Shiotae3199732013-11-25 16:14:43 -0800259 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800260 class FlowEntryWrapper {
Ray Milkey8e5170e2014-04-02 12:09:55 -0700261 FlowEntryId flowEntryId;
262 // TODO: fix when FlowSynchronizer is refactored
263 // IFlowEntry iFlowEntry;
264 OFFlowStatisticsReply statisticsReply;
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800265
Brian O'Connore46492e2013-11-14 21:11:50 -0800266
Ray Milkey8e5170e2014-04-02 12:09:55 -0700267 // TODO: fix when FlowSynchronizer is refactored
268 /*
269 public FlowEntryWrapper(IFlowEntry entry) {
270 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
271 iFlowEntry = entry;
272 }
273 */
Brian O'Connora8e49802013-10-30 20:49:59 -0700274
Ray Milkey8e5170e2014-04-02 12:09:55 -0700275 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
276 flowEntryId = new FlowEntryId(entry.getCookie());
277 statisticsReply = entry;
278 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700279
Ray Milkey8e5170e2014-04-02 12:09:55 -0700280 /**
281 * Install this FlowEntry to a switch via FlowPusher.
282 *
283 * @param sw Switch to which flow will be installed.
284 */
285 double dbTime, extractTime, pushTime;
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800286
Ray Milkey8e5170e2014-04-02 12:09:55 -0700287 public void addToSwitch(IOFSwitch sw) {
288 if (statisticsReply != null) {
289 log.error("Error adding existing flow entry {} to sw {}",
290 statisticsReply.getCookie(), sw.getId());
291 return;
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800292 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800293
Ray Milkey8e5170e2014-04-02 12:09:55 -0700294 double startDB = System.nanoTime();
295 // Get the Flow Entry state from the Network Graph
296 // TODO: fix when FlowSynchronizer is refactored
297 /*
298 if (iFlowEntry == null) {
299 try {
300 // TODO: fix when FlowSynchronizer is refactored
301 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
302 } catch (Exception e) {
303 log.error("Error finding flow entry {} in Network Graph",
304 flowEntryId);
305 return;
306 }
307 }
308 */
309 dbTime = System.nanoTime() - startDB;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800310
Ray Milkey8e5170e2014-04-02 12:09:55 -0700311 //
312 // TODO: The old FlowDatabaseOperation class is gone, so the code
313 //
314 /*
315 double startExtract = System.nanoTime();
316 FlowEntry flowEntry =
317 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
318 if (flowEntry == null) {
319 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
320 flowEntryId, sw.getId());
321 return;
322 }
323 extractTime = System.nanoTime() - startExtract;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800324
Ray Milkey8e5170e2014-04-02 12:09:55 -0700325 double startPush = System.nanoTime();
326 pusher.pushFlowEntry(sw, flowEntry, MsgPriority.HIGH);
327 pushTime = System.nanoTime() - startPush;
328 */
329 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700330
Ray Milkey8e5170e2014-04-02 12:09:55 -0700331 /**
332 * Remove this FlowEntry from a switch via FlowPusher.
333 *
334 * @param sw Switch from which flow will be removed.
335 */
336 public void removeFromSwitch(IOFSwitch sw) {
337 if (statisticsReply == null) {
338 log.error("Error removing non-existent flow entry {} from sw {}",
339 flowEntryId, sw.getId());
340 return;
341 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800342
Ray Milkey8e5170e2014-04-02 12:09:55 -0700343 // Convert Statistics Reply to Flow Mod, then write it
344 OFFlowMod fm = new OFFlowMod();
345 fm.setCookie(statisticsReply.getCookie());
346 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
347 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
348 fm.setMatch(statisticsReply.getMatch());
349 fm.setPriority(statisticsReply.getPriority());
350 fm.setOutPort(OFPort.OFPP_NONE);
Brian O'Connora8e49802013-10-30 20:49:59 -0700351
Ray Milkey8e5170e2014-04-02 12:09:55 -0700352 pusher.add(sw, fm, MsgPriority.HIGH);
353 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700354
Ray Milkey8e5170e2014-04-02 12:09:55 -0700355 /**
356 * Return the hash code of the Flow Entry ID
357 */
358 @Override
359 public int hashCode() {
360 return flowEntryId.hashCode();
361 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800362
Ray Milkey8e5170e2014-04-02 12:09:55 -0700363 /**
364 * Returns true of the object is another Flow Entry ID with
365 * the same value; otherwise, returns false.
366 *
367 * @param Object to compare
368 * @return true if the object has the same Flow Entry ID.
369 */
370 @Override
371 public boolean equals(Object obj) {
372 if (obj != null && obj.getClass() == this.getClass()) {
373 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
374 // TODO: we need to actually compare the match + actions
375 return this.flowEntryId.equals(entry.flowEntryId);
376 }
377 return false;
378 }
379
380 @Override
381 public String toString() {
382 return flowEntryId.toString();
383 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800384 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800385}