blob: 52eeff25cb7aeacb9ec0f971c7ee3b897f4f9d09 [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
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;
Jonathan Hart23701d12014-04-03 10:45:48 -070027import net.onrc.onos.core.flowprogrammer.IFlowPusherService.MsgPriority;
28import net.onrc.onos.core.util.Dpid;
29import net.onrc.onos.core.util.FlowEntry;
30import net.onrc.onos.core.util.FlowEntryId;
Brian O'Connora8e49802013-10-30 20:49:59 -070031
Naoki Shiotae3199732013-11-25 16:14:43 -080032/**
Naoki Shiotab485d412013-11-26 12:04:19 -080033 * FlowSynchronizer is an implementation of FlowSyncService.
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -080034 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
35 * tables from switches and compare them with GraphDB to drop unnecessary
36 * flows and/or to install missing flows.
Naoki Shiotae3199732013-11-25 16:14:43 -080037 *
Ray Milkey8e5170e2014-04-02 12:09:55 -070038 * @author Brian
Naoki Shiotae3199732013-11-25 16:14:43 -080039 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080040public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070041
Brian O'Connorea1efbe2013-11-25 22:57:43 -080042 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080043
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -070044 // TODO: fix when FlowSynchronizer is refactored
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070045 // private DBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080046 protected IFlowPusherService pusher;
Ray Milkey8e5170e2014-04-02 12:09:55 -070047 private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080048
49 public FlowSynchronizer() {
Ray Milkey8e5170e2014-04-02 12:09:55 -070050 // TODO: fix when FlowSynchronizer is refactored
51 // dbHandler = GraphDBManager.getDBOperation();
52 switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>();
Brian O'Connore46492e2013-11-14 21:11:50 -080053 }
54
Brian O'Connorea1efbe2013-11-25 22:57:43 -080055 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -080056 public Future<SyncResult> synchronize(IOFSwitch sw) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070057 Synchronizer sync = new Synchronizer(sw);
58 FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync);
59 switchThreads.put(sw, task);
60 task.run();
61 return task;
Brian O'Connore46492e2013-11-14 21:11:50 -080062 }
Ray Milkey8e5170e2014-04-02 12:09:55 -070063
Brian O'Connore46492e2013-11-14 21:11:50 -080064 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080065 public void interrupt(IOFSwitch sw) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070066 FutureTask<SyncResult> t = switchThreads.remove(sw);
67 if (t != null) {
68 t.cancel(true);
69 }
Brian O'Connore46492e2013-11-14 21:11:50 -080070 }
71
Naoki Shiotab485d412013-11-26 12:04:19 -080072 /**
73 * Initialize Synchronizer.
Ray Milkey8e5170e2014-04-02 12:09:55 -070074 *
Naoki Shiotab485d412013-11-26 12:04:19 -080075 * @param pusherService FlowPusherService used for sending messages.
76 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080077 public void init(IFlowPusherService pusherService) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070078 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080079 }
80
Naoki Shiotae3199732013-11-25 16:14:43 -080081 /**
82 * Synchronizer represents main thread of synchronization.
Naoki Shiotae3199732013-11-25 16:14:43 -080083 *
Ray Milkey8e5170e2014-04-02 12:09:55 -070084 * @author Brian
Naoki Shiotae3199732013-11-25 16:14:43 -080085 */
Ray Milkey8e5170e2014-04-02 12:09:55 -070086 protected class Synchronizer implements Callable<SyncResult> {
87 IOFSwitch sw;
88 // TODO: fix when FlowSynchronizer is refactored
89 // ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080090
Ray Milkey8e5170e2014-04-02 12:09:55 -070091 public Synchronizer(IOFSwitch sw) {
92 this.sw = sw;
93 Dpid dpid = new Dpid(sw.getId());
94 // TODO: fix when FlowSynchronizer is refactored
95 // this.swObj = dbHandler.searchSwitch(dpid.toString());
96 }
Brian O'Connore46492e2013-11-14 21:11:50 -080097
Ray Milkey8e5170e2014-04-02 12:09:55 -070098 double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime;
Brian O'Connore46492e2013-11-14 21:11:50 -080099
Ray Milkey8e5170e2014-04-02 12:09:55 -0700100 @Override
101 public SyncResult call() {
102 pusher.suspend(sw);
103 try {
104 long start = System.nanoTime();
105 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
106 long step1 = System.nanoTime();
107 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
108 if (switchEntries == null) {
109 log.debug("getFlowEntriesFromSwitch() failed");
110 return null;
111 }
112 long step2 = System.nanoTime();
113 SyncResult result = compare(graphEntries, switchEntries);
114 long step3 = System.nanoTime();
115 graphIDTime = (step1 - start);
116 switchTime = (step2 - step1);
117 compareTime = (step3 - step2);
118 totalTime = (step3 - start);
119 outputTime();
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800120
Ray Milkey8e5170e2014-04-02 12:09:55 -0700121 return result;
122 } finally {
123 pusher.resume(sw);
124 }
125 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800126
Ray Milkey8e5170e2014-04-02 12:09:55 -0700127 private void outputTime() {
128 double div = Math.pow(10, 6); //convert nanoseconds to ms
129 graphIDTime /= div;
130 switchTime /= div;
131 compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
132 graphEntryTime /= div;
133 extractTime /= div;
134 pushTime /= div;
135 totalTime /= div;
136 log.debug("Sync time (ms):{},{},{},{},{},{},{}"
137 , graphIDTime
138 , switchTime
139 , compareTime
140 , graphEntryTime
141 , extractTime
142 , pushTime
143 , totalTime);
144 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700145
Ray Milkey8e5170e2014-04-02 12:09:55 -0700146 /**
147 * Compare flows entries in GraphDB and switch to pick up necessary
148 * messages.
149 * After picking up, picked messages are added to FlowPusher.
150 *
151 * @param graphEntries Flow entries in GraphDB.
152 * @param switchEntries Flow entries in switch.
153 */
154 private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
155 int added = 0, removed = 0, skipped = 0;
156 for (FlowEntryWrapper entry : switchEntries) {
157 if (graphEntries.contains(entry)) {
158 graphEntries.remove(entry);
159 skipped++;
160 } else {
161 // remove flow entry from the switch
162 entry.removeFromSwitch(sw);
163 removed++;
164 }
165 }
166 for (FlowEntryWrapper entry : graphEntries) {
167 // add flow entry to switch
168 entry.addToSwitch(sw);
169 graphEntryTime += entry.dbTime;
170 extractTime += entry.extractTime;
171 pushTime += entry.pushTime;
172 added++;
173 }
174 log.debug("Flow entries added {}, " +
175 "Flow entries removed {}, " +
176 "Flow entries skipped {}"
177 , added
178 , removed
179 , skipped);
Brian O'Connore46492e2013-11-14 21:11:50 -0800180
Ray Milkey8e5170e2014-04-02 12:09:55 -0700181 return new SyncResult(added, removed, skipped);
182 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700183
Ray Milkey8e5170e2014-04-02 12:09:55 -0700184 /**
185 * Read GraphDB to get FlowEntries associated with a switch.
186 *
187 * @return set of FlowEntries
188 */
189 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
190 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700191
Ray Milkey8e5170e2014-04-02 12:09:55 -0700192 // TODO: fix when FlowSynchronizer is refactored
193 /*
194 for(IFlowEntry entry : swObj.getFlowEntries()) {
195 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
196 entries.add(fe);
197 }
198 */
199 return entries;
200 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700201
Ray Milkey8e5170e2014-04-02 12:09:55 -0700202 /**
203 * Read flow table from switch and derive FlowEntries from table.
204 *
205 * @return set of FlowEntries
206 */
207 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700208
Ray Milkey8e5170e2014-04-02 12:09:55 -0700209 int lengthU = 0;
210 OFMatch match = new OFMatch();
211 match.setWildcards(OFMatch.OFPFW_ALL);
Brian O'Connore46492e2013-11-14 21:11:50 -0800212
Ray Milkey8e5170e2014-04-02 12:09:55 -0700213 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
214 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
215 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
216 stat.setMatch(match);
217 List<OFStatistics> stats = new ArrayList<OFStatistics>();
218 stats.add(stat);
219 lengthU += stat.getLength();
220
221 OFStatisticsRequest req = new OFStatisticsRequest();
222 req.setStatisticType(OFStatisticsType.FLOW);
223 req.setStatistics(stats);
224 lengthU += req.getLengthU();
225 req.setLengthU(lengthU);
226
227 List<OFStatistics> entries = null;
228 try {
229 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
230 entries = dfuture.get();
231 } catch (IOException e) {
232 // TODO Auto-generated catch block
233 e.printStackTrace();
234 return null;
235 } catch (InterruptedException e) {
236 // TODO Auto-generated catch block
237 e.printStackTrace();
238 return null;
239 } catch (ExecutionException e) {
240 // TODO Auto-generated catch block
241 e.printStackTrace();
242 return null;
243 }
244
245 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
246 for (OFStatistics result : entries) {
247 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
248 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
249 results.add(fe);
250 }
251 return results;
252 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800253
Brian O'Connora8e49802013-10-30 20:49:59 -0700254 }
255
Naoki Shiotae3199732013-11-25 16:14:43 -0800256 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800257 * FlowEntryWrapper represents abstract FlowEntry which is embodied
258 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800259 *
Ray Milkey8e5170e2014-04-02 12:09:55 -0700260 * @author Brian
Naoki Shiotae3199732013-11-25 16:14:43 -0800261 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800262 class FlowEntryWrapper {
Ray Milkey8e5170e2014-04-02 12:09:55 -0700263 FlowEntryId flowEntryId;
264 // TODO: fix when FlowSynchronizer is refactored
265 // IFlowEntry iFlowEntry;
266 OFFlowStatisticsReply statisticsReply;
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800267
Brian O'Connore46492e2013-11-14 21:11:50 -0800268
Ray Milkey8e5170e2014-04-02 12:09:55 -0700269 // TODO: fix when FlowSynchronizer is refactored
270 /*
271 public FlowEntryWrapper(IFlowEntry entry) {
272 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
273 iFlowEntry = entry;
274 }
275 */
Brian O'Connora8e49802013-10-30 20:49:59 -0700276
Ray Milkey8e5170e2014-04-02 12:09:55 -0700277 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
278 flowEntryId = new FlowEntryId(entry.getCookie());
279 statisticsReply = entry;
280 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700281
Ray Milkey8e5170e2014-04-02 12:09:55 -0700282 /**
283 * Install this FlowEntry to a switch via FlowPusher.
284 *
285 * @param sw Switch to which flow will be installed.
286 */
287 double dbTime, extractTime, pushTime;
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800288
Ray Milkey8e5170e2014-04-02 12:09:55 -0700289 public void addToSwitch(IOFSwitch sw) {
290 if (statisticsReply != null) {
291 log.error("Error adding existing flow entry {} to sw {}",
292 statisticsReply.getCookie(), sw.getId());
293 return;
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800294 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800295
Ray Milkey8e5170e2014-04-02 12:09:55 -0700296 double startDB = System.nanoTime();
297 // Get the Flow Entry state from the Network Graph
298 // TODO: fix when FlowSynchronizer is refactored
299 /*
300 if (iFlowEntry == null) {
301 try {
302 // TODO: fix when FlowSynchronizer is refactored
303 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
304 } catch (Exception e) {
305 log.error("Error finding flow entry {} in Network Graph",
306 flowEntryId);
307 return;
308 }
309 }
310 */
311 dbTime = System.nanoTime() - startDB;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800312
Ray Milkey8e5170e2014-04-02 12:09:55 -0700313 //
314 // TODO: The old FlowDatabaseOperation class is gone, so the code
315 //
316 /*
317 double startExtract = System.nanoTime();
318 FlowEntry flowEntry =
319 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
320 if (flowEntry == null) {
321 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
322 flowEntryId, sw.getId());
323 return;
324 }
325 extractTime = System.nanoTime() - startExtract;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800326
Ray Milkey8e5170e2014-04-02 12:09:55 -0700327 double startPush = System.nanoTime();
328 pusher.pushFlowEntry(sw, flowEntry, MsgPriority.HIGH);
329 pushTime = System.nanoTime() - startPush;
330 */
331 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700332
Ray Milkey8e5170e2014-04-02 12:09:55 -0700333 /**
334 * Remove this FlowEntry from a switch via FlowPusher.
335 *
336 * @param sw Switch from which flow will be removed.
337 */
338 public void removeFromSwitch(IOFSwitch sw) {
339 if (statisticsReply == null) {
340 log.error("Error removing non-existent flow entry {} from sw {}",
341 flowEntryId, sw.getId());
342 return;
343 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800344
Ray Milkey8e5170e2014-04-02 12:09:55 -0700345 // Convert Statistics Reply to Flow Mod, then write it
346 OFFlowMod fm = new OFFlowMod();
347 fm.setCookie(statisticsReply.getCookie());
348 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
349 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
350 fm.setMatch(statisticsReply.getMatch());
351 fm.setPriority(statisticsReply.getPriority());
352 fm.setOutPort(OFPort.OFPP_NONE);
Brian O'Connora8e49802013-10-30 20:49:59 -0700353
Ray Milkey8e5170e2014-04-02 12:09:55 -0700354 pusher.add(sw, fm, MsgPriority.HIGH);
355 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700356
Ray Milkey8e5170e2014-04-02 12:09:55 -0700357 /**
358 * Return the hash code of the Flow Entry ID
359 */
360 @Override
361 public int hashCode() {
362 return flowEntryId.hashCode();
363 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800364
Ray Milkey8e5170e2014-04-02 12:09:55 -0700365 /**
366 * Returns true of the object is another Flow Entry ID with
367 * the same value; otherwise, returns false.
368 *
369 * @param Object to compare
370 * @return true if the object has the same Flow Entry ID.
371 */
372 @Override
373 public boolean equals(Object obj) {
374 if (obj != null && obj.getClass() == this.getClass()) {
375 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
376 // TODO: we need to actually compare the match + actions
377 return this.flowEntryId.equals(entry.flowEntryId);
378 }
379 return false;
380 }
381
382 @Override
383 public String toString() {
384 return flowEntryId.toString();
385 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800386 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800387}