blob: af3cc70f4642b4369168a0ef885779d6c73f1f58 [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 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080036public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070037
Brian O'Connorea1efbe2013-11-25 22:57:43 -080038 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080039
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -070040 // TODO: fix when FlowSynchronizer is refactored
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070041 // private DBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080042 protected IFlowPusherService pusher;
Ray Milkey8e5170e2014-04-02 12:09:55 -070043 private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080044
45 public FlowSynchronizer() {
Ray Milkey8e5170e2014-04-02 12:09:55 -070046 // TODO: fix when FlowSynchronizer is refactored
47 // dbHandler = GraphDBManager.getDBOperation();
48 switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>();
Brian O'Connore46492e2013-11-14 21:11:50 -080049 }
50
Brian O'Connorea1efbe2013-11-25 22:57:43 -080051 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -080052 public Future<SyncResult> synchronize(IOFSwitch sw) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070053 Synchronizer sync = new Synchronizer(sw);
54 FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync);
55 switchThreads.put(sw, task);
56 task.run();
57 return task;
Brian O'Connore46492e2013-11-14 21:11:50 -080058 }
Ray Milkey8e5170e2014-04-02 12:09:55 -070059
Brian O'Connore46492e2013-11-14 21:11:50 -080060 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080061 public void interrupt(IOFSwitch sw) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070062 FutureTask<SyncResult> t = switchThreads.remove(sw);
63 if (t != null) {
64 t.cancel(true);
65 }
Brian O'Connore46492e2013-11-14 21:11:50 -080066 }
67
Naoki Shiotab485d412013-11-26 12:04:19 -080068 /**
69 * Initialize Synchronizer.
Ray Milkey8e5170e2014-04-02 12:09:55 -070070 *
Naoki Shiotab485d412013-11-26 12:04:19 -080071 * @param pusherService FlowPusherService used for sending messages.
72 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080073 public void init(IFlowPusherService pusherService) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070074 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080075 }
76
Naoki Shiotae3199732013-11-25 16:14:43 -080077 /**
78 * Synchronizer represents main thread of synchronization.
Naoki Shiotae3199732013-11-25 16:14:43 -080079 */
Ray Milkey8e5170e2014-04-02 12:09:55 -070080 protected class Synchronizer implements Callable<SyncResult> {
81 IOFSwitch sw;
82 // TODO: fix when FlowSynchronizer is refactored
83 // ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080084
Ray Milkey8e5170e2014-04-02 12:09:55 -070085 public Synchronizer(IOFSwitch sw) {
86 this.sw = sw;
Ray Milkey8e5170e2014-04-02 12:09:55 -070087 // TODO: fix when FlowSynchronizer is refactored
Pavlin Radoslavov3255ae52014-04-09 13:01:54 -070088 // Dpid dpid = new Dpid(sw.getId());
Ray Milkey8e5170e2014-04-02 12:09:55 -070089 // this.swObj = dbHandler.searchSwitch(dpid.toString());
90 }
Brian O'Connore46492e2013-11-14 21:11:50 -080091
Ray Milkey8e5170e2014-04-02 12:09:55 -070092 double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime;
Brian O'Connore46492e2013-11-14 21:11:50 -080093
Ray Milkey8e5170e2014-04-02 12:09:55 -070094 @Override
95 public SyncResult call() {
96 pusher.suspend(sw);
97 try {
98 long start = System.nanoTime();
99 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
100 long step1 = System.nanoTime();
101 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
102 if (switchEntries == null) {
103 log.debug("getFlowEntriesFromSwitch() failed");
104 return null;
105 }
106 long step2 = System.nanoTime();
107 SyncResult result = compare(graphEntries, switchEntries);
108 long step3 = System.nanoTime();
109 graphIDTime = (step1 - start);
110 switchTime = (step2 - step1);
111 compareTime = (step3 - step2);
112 totalTime = (step3 - start);
113 outputTime();
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800114
Ray Milkey8e5170e2014-04-02 12:09:55 -0700115 return result;
116 } finally {
117 pusher.resume(sw);
118 }
119 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800120
Ray Milkey8e5170e2014-04-02 12:09:55 -0700121 private void outputTime() {
122 double div = Math.pow(10, 6); //convert nanoseconds to ms
123 graphIDTime /= div;
124 switchTime /= div;
125 compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
126 graphEntryTime /= div;
127 extractTime /= div;
128 pushTime /= div;
129 totalTime /= div;
130 log.debug("Sync time (ms):{},{},{},{},{},{},{}"
131 , graphIDTime
132 , switchTime
133 , compareTime
134 , graphEntryTime
135 , extractTime
136 , pushTime
137 , totalTime);
138 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700139
Ray Milkey8e5170e2014-04-02 12:09:55 -0700140 /**
141 * Compare flows entries in GraphDB and switch to pick up necessary
142 * messages.
143 * After picking up, picked messages are added to FlowPusher.
144 *
145 * @param graphEntries Flow entries in GraphDB.
146 * @param switchEntries Flow entries in switch.
147 */
148 private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
149 int added = 0, removed = 0, skipped = 0;
150 for (FlowEntryWrapper entry : switchEntries) {
151 if (graphEntries.contains(entry)) {
152 graphEntries.remove(entry);
153 skipped++;
154 } else {
155 // remove flow entry from the switch
156 entry.removeFromSwitch(sw);
157 removed++;
158 }
159 }
160 for (FlowEntryWrapper entry : graphEntries) {
161 // add flow entry to switch
162 entry.addToSwitch(sw);
163 graphEntryTime += entry.dbTime;
164 extractTime += entry.extractTime;
165 pushTime += entry.pushTime;
166 added++;
167 }
168 log.debug("Flow entries added {}, " +
169 "Flow entries removed {}, " +
170 "Flow entries skipped {}"
171 , added
172 , removed
173 , skipped);
Brian O'Connore46492e2013-11-14 21:11:50 -0800174
Ray Milkey8e5170e2014-04-02 12:09:55 -0700175 return new SyncResult(added, removed, skipped);
176 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700177
Ray Milkey8e5170e2014-04-02 12:09:55 -0700178 /**
179 * Read GraphDB to get FlowEntries associated with a switch.
180 *
181 * @return set of FlowEntries
182 */
183 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
184 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700185
Ray Milkey8e5170e2014-04-02 12:09:55 -0700186 // TODO: fix when FlowSynchronizer is refactored
187 /*
188 for(IFlowEntry entry : swObj.getFlowEntries()) {
189 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
190 entries.add(fe);
191 }
192 */
193 return entries;
194 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700195
Ray Milkey8e5170e2014-04-02 12:09:55 -0700196 /**
197 * Read flow table from switch and derive FlowEntries from table.
198 *
199 * @return set of FlowEntries
200 */
201 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700202
Ray Milkey8e5170e2014-04-02 12:09:55 -0700203 int lengthU = 0;
204 OFMatch match = new OFMatch();
205 match.setWildcards(OFMatch.OFPFW_ALL);
Brian O'Connore46492e2013-11-14 21:11:50 -0800206
Ray Milkey8e5170e2014-04-02 12:09:55 -0700207 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
208 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
209 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
210 stat.setMatch(match);
211 List<OFStatistics> stats = new ArrayList<OFStatistics>();
212 stats.add(stat);
213 lengthU += stat.getLength();
214
215 OFStatisticsRequest req = new OFStatisticsRequest();
216 req.setStatisticType(OFStatisticsType.FLOW);
217 req.setStatistics(stats);
218 lengthU += req.getLengthU();
219 req.setLengthU(lengthU);
220
221 List<OFStatistics> entries = null;
222 try {
223 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
224 entries = dfuture.get();
225 } catch (IOException e) {
Yuta HIGUCHIe933a282014-07-13 21:02:29 -0700226 log.error("Error getting statistics", e);
Ray Milkey8e5170e2014-04-02 12:09:55 -0700227 return null;
228 } catch (InterruptedException e) {
Yuta HIGUCHIe933a282014-07-13 21:02:29 -0700229 log.error("Error getting statistics", e);
Ray Milkey8e5170e2014-04-02 12:09:55 -0700230 return null;
231 } catch (ExecutionException e) {
Yuta HIGUCHIe933a282014-07-13 21:02:29 -0700232 log.error("Error getting statistics", e);
Ray Milkey8e5170e2014-04-02 12:09:55 -0700233 return null;
234 }
235
236 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
237 for (OFStatistics result : entries) {
238 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
239 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
240 results.add(fe);
241 }
242 return results;
243 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800244
Brian O'Connora8e49802013-10-30 20:49:59 -0700245 }
246
Naoki Shiotae3199732013-11-25 16:14:43 -0800247 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800248 * FlowEntryWrapper represents abstract FlowEntry which is embodied
249 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800250 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800251 class FlowEntryWrapper {
Ray Milkey8e5170e2014-04-02 12:09:55 -0700252 FlowEntryId flowEntryId;
253 // TODO: fix when FlowSynchronizer is refactored
254 // IFlowEntry iFlowEntry;
255 OFFlowStatisticsReply statisticsReply;
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800256
Brian O'Connore46492e2013-11-14 21:11:50 -0800257
Ray Milkey8e5170e2014-04-02 12:09:55 -0700258 // TODO: fix when FlowSynchronizer is refactored
259 /*
260 public FlowEntryWrapper(IFlowEntry entry) {
261 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
262 iFlowEntry = entry;
263 }
264 */
Brian O'Connora8e49802013-10-30 20:49:59 -0700265
Ray Milkey8e5170e2014-04-02 12:09:55 -0700266 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
267 flowEntryId = new FlowEntryId(entry.getCookie());
268 statisticsReply = entry;
269 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700270
Ray Milkey8e5170e2014-04-02 12:09:55 -0700271 /**
272 * Install this FlowEntry to a switch via FlowPusher.
273 *
274 * @param sw Switch to which flow will be installed.
275 */
276 double dbTime, extractTime, pushTime;
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800277
Ray Milkey8e5170e2014-04-02 12:09:55 -0700278 public void addToSwitch(IOFSwitch sw) {
279 if (statisticsReply != null) {
280 log.error("Error adding existing flow entry {} to sw {}",
281 statisticsReply.getCookie(), sw.getId());
282 return;
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800283 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800284
Ray Milkey8e5170e2014-04-02 12:09:55 -0700285 double startDB = System.nanoTime();
286 // Get the Flow Entry state from the Network Graph
287 // TODO: fix when FlowSynchronizer is refactored
288 /*
289 if (iFlowEntry == null) {
290 try {
291 // TODO: fix when FlowSynchronizer is refactored
292 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
293 } catch (Exception e) {
294 log.error("Error finding flow entry {} in Network Graph",
295 flowEntryId);
296 return;
297 }
298 }
299 */
300 dbTime = System.nanoTime() - startDB;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800301
Ray Milkey8e5170e2014-04-02 12:09:55 -0700302 //
303 // TODO: The old FlowDatabaseOperation class is gone, so the code
304 //
305 /*
306 double startExtract = System.nanoTime();
307 FlowEntry flowEntry =
308 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
309 if (flowEntry == null) {
310 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
311 flowEntryId, sw.getId());
312 return;
313 }
314 extractTime = System.nanoTime() - startExtract;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800315
Ray Milkey8e5170e2014-04-02 12:09:55 -0700316 double startPush = System.nanoTime();
317 pusher.pushFlowEntry(sw, flowEntry, MsgPriority.HIGH);
318 pushTime = System.nanoTime() - startPush;
319 */
320 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700321
Ray Milkey8e5170e2014-04-02 12:09:55 -0700322 /**
323 * Remove this FlowEntry from a switch via FlowPusher.
324 *
325 * @param sw Switch from which flow will be removed.
326 */
327 public void removeFromSwitch(IOFSwitch sw) {
328 if (statisticsReply == null) {
329 log.error("Error removing non-existent flow entry {} from sw {}",
330 flowEntryId, sw.getId());
331 return;
332 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800333
Ray Milkey8e5170e2014-04-02 12:09:55 -0700334 // Convert Statistics Reply to Flow Mod, then write it
335 OFFlowMod fm = new OFFlowMod();
336 fm.setCookie(statisticsReply.getCookie());
337 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
338 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
339 fm.setMatch(statisticsReply.getMatch());
340 fm.setPriority(statisticsReply.getPriority());
341 fm.setOutPort(OFPort.OFPP_NONE);
Brian O'Connora8e49802013-10-30 20:49:59 -0700342
Ray Milkey8e5170e2014-04-02 12:09:55 -0700343 pusher.add(sw, fm, MsgPriority.HIGH);
344 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700345
Ray Milkey8e5170e2014-04-02 12:09:55 -0700346 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700347 * Return the hash code of the Flow Entry ID.
Ray Milkey8e5170e2014-04-02 12:09:55 -0700348 */
349 @Override
350 public int hashCode() {
351 return flowEntryId.hashCode();
352 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800353
Ray Milkey8e5170e2014-04-02 12:09:55 -0700354 /**
355 * Returns true of the object is another Flow Entry ID with
356 * the same value; otherwise, returns false.
357 *
Sho SHIMIZUa1199fa2014-06-10 18:11:12 -0700358 * @param obj to compare
Ray Milkey8e5170e2014-04-02 12:09:55 -0700359 * @return true if the object has the same Flow Entry ID.
360 */
361 @Override
362 public boolean equals(Object obj) {
363 if (obj != null && obj.getClass() == this.getClass()) {
364 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
365 // TODO: we need to actually compare the match + actions
366 return this.flowEntryId.equals(entry.flowEntryId);
367 }
368 return false;
369 }
370
371 @Override
372 public String toString() {
373 return flowEntryId.toString();
374 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800375 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800376}