blob: 7b2bfd0bb303cecd65ec9f5dea09d40218d361e2 [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
Brian O'Connora8e49802013-10-30 20:49:59 -07003import java.util.ArrayList;
Brian O'Connora8e49802013-10-30 20:49:59 -07004import java.util.HashMap;
5import java.util.HashSet;
6import java.util.List;
7import java.util.Map;
8import java.util.Set;
Naoki Shiota2bdda572013-12-09 15:05:21 -08009import java.util.concurrent.Callable;
Brian O'Connora8e49802013-10-30 20:49:59 -070010import java.util.concurrent.Future;
Naoki Shiota2bdda572013-12-09 15:05:21 -080011import java.util.concurrent.FutureTask;
Brian O'Connora8e49802013-10-30 20:49:59 -070012
Jonathan Harta99ec672014-04-03 11:30:34 -070013import net.floodlightcontroller.core.IOFSwitch;
Jonathan Harta99ec672014-04-03 11:30:34 -070014import net.onrc.onos.core.util.FlowEntryId;
15
Brian O'Connor0d6ba512013-11-05 15:17:44 -080016import org.openflow.protocol.OFFlowMod;
Brian O'Connora8e49802013-10-30 20:49:59 -070017import org.openflow.protocol.OFMatch;
Brian O'Connor0d6ba512013-11-05 15:17:44 -080018import org.openflow.protocol.OFPort;
Brian O'Connora8e49802013-10-30 20:49:59 -070019import org.openflow.protocol.OFStatisticsRequest;
20import org.openflow.protocol.statistics.OFFlowStatisticsReply;
21import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
22import org.openflow.protocol.statistics.OFStatistics;
23import org.openflow.protocol.statistics.OFStatisticsType;
24import org.slf4j.Logger;
25import org.slf4j.LoggerFactory;
26
Naoki Shiotae3199732013-11-25 16:14:43 -080027/**
Naoki Shiotab485d412013-11-26 12:04:19 -080028 * FlowSynchronizer is an implementation of FlowSyncService.
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -080029 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
30 * tables from switches and compare them with GraphDB to drop unnecessary
31 * flows and/or to install missing flows.
Naoki Shiotae3199732013-11-25 16:14:43 -080032 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080033public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070034
Brian O'Connorea1efbe2013-11-25 22:57:43 -080035 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080036
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -070037 // TODO: fix when FlowSynchronizer is refactored
Pavlin Radoslavov697f6982014-03-26 15:55:19 -070038 // private DBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080039 protected IFlowPusherService pusher;
Ray Milkey8e5170e2014-04-02 12:09:55 -070040 private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080041
42 public FlowSynchronizer() {
Ray Milkey8e5170e2014-04-02 12:09:55 -070043 // TODO: fix when FlowSynchronizer is refactored
44 // dbHandler = GraphDBManager.getDBOperation();
45 switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>();
Brian O'Connore46492e2013-11-14 21:11:50 -080046 }
47
Brian O'Connorea1efbe2013-11-25 22:57:43 -080048 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -080049 public Future<SyncResult> synchronize(IOFSwitch sw) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070050 Synchronizer sync = new Synchronizer(sw);
51 FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync);
52 switchThreads.put(sw, task);
53 task.run();
54 return task;
Brian O'Connore46492e2013-11-14 21:11:50 -080055 }
Ray Milkey8e5170e2014-04-02 12:09:55 -070056
Brian O'Connore46492e2013-11-14 21:11:50 -080057 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080058 public void interrupt(IOFSwitch sw) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070059 FutureTask<SyncResult> t = switchThreads.remove(sw);
60 if (t != null) {
61 t.cancel(true);
62 }
Brian O'Connore46492e2013-11-14 21:11:50 -080063 }
64
Naoki Shiotab485d412013-11-26 12:04:19 -080065 /**
66 * Initialize Synchronizer.
Ray Milkey8e5170e2014-04-02 12:09:55 -070067 *
Naoki Shiotab485d412013-11-26 12:04:19 -080068 * @param pusherService FlowPusherService used for sending messages.
69 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080070 public void init(IFlowPusherService pusherService) {
Ray Milkey8e5170e2014-04-02 12:09:55 -070071 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080072 }
73
Naoki Shiotae3199732013-11-25 16:14:43 -080074 /**
75 * Synchronizer represents main thread of synchronization.
Naoki Shiotae3199732013-11-25 16:14:43 -080076 */
Ray Milkey8e5170e2014-04-02 12:09:55 -070077 protected class Synchronizer implements Callable<SyncResult> {
78 IOFSwitch sw;
79 // TODO: fix when FlowSynchronizer is refactored
80 // ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080081
Ray Milkey8e5170e2014-04-02 12:09:55 -070082 public Synchronizer(IOFSwitch sw) {
83 this.sw = sw;
Ray Milkey8e5170e2014-04-02 12:09:55 -070084 // TODO: fix when FlowSynchronizer is refactored
Pavlin Radoslavov3255ae52014-04-09 13:01:54 -070085 // Dpid dpid = new Dpid(sw.getId());
Ray Milkey8e5170e2014-04-02 12:09:55 -070086 // this.swObj = dbHandler.searchSwitch(dpid.toString());
87 }
Brian O'Connore46492e2013-11-14 21:11:50 -080088
Ray Milkey8e5170e2014-04-02 12:09:55 -070089 double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime;
Brian O'Connore46492e2013-11-14 21:11:50 -080090
Ray Milkey8e5170e2014-04-02 12:09:55 -070091 @Override
92 public SyncResult call() {
93 pusher.suspend(sw);
94 try {
95 long start = System.nanoTime();
96 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
97 long step1 = System.nanoTime();
98 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
Ray Milkey8e5170e2014-04-02 12:09:55 -070099 long step2 = System.nanoTime();
100 SyncResult result = compare(graphEntries, switchEntries);
101 long step3 = System.nanoTime();
102 graphIDTime = (step1 - start);
103 switchTime = (step2 - step1);
104 compareTime = (step3 - step2);
105 totalTime = (step3 - start);
106 outputTime();
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800107
Ray Milkey8e5170e2014-04-02 12:09:55 -0700108 return result;
109 } finally {
110 pusher.resume(sw);
111 }
112 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800113
Ray Milkey8e5170e2014-04-02 12:09:55 -0700114 private void outputTime() {
115 double div = Math.pow(10, 6); //convert nanoseconds to ms
116 graphIDTime /= div;
117 switchTime /= div;
118 compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
119 graphEntryTime /= div;
120 extractTime /= div;
121 pushTime /= div;
122 totalTime /= div;
123 log.debug("Sync time (ms):{},{},{},{},{},{},{}"
124 , graphIDTime
125 , switchTime
126 , compareTime
127 , graphEntryTime
128 , extractTime
129 , pushTime
130 , totalTime);
131 }
Pavlin Radoslavov4cf8ee52014-03-26 18:19:58 -0700132
Ray Milkey8e5170e2014-04-02 12:09:55 -0700133 /**
134 * Compare flows entries in GraphDB and switch to pick up necessary
135 * messages.
136 * After picking up, picked messages are added to FlowPusher.
137 *
138 * @param graphEntries Flow entries in GraphDB.
139 * @param switchEntries Flow entries in switch.
140 */
141 private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
142 int added = 0, removed = 0, skipped = 0;
143 for (FlowEntryWrapper entry : switchEntries) {
144 if (graphEntries.contains(entry)) {
145 graphEntries.remove(entry);
146 skipped++;
147 } else {
148 // remove flow entry from the switch
149 entry.removeFromSwitch(sw);
150 removed++;
151 }
152 }
153 for (FlowEntryWrapper entry : graphEntries) {
154 // add flow entry to switch
155 entry.addToSwitch(sw);
156 graphEntryTime += entry.dbTime;
157 extractTime += entry.extractTime;
158 pushTime += entry.pushTime;
159 added++;
160 }
161 log.debug("Flow entries added {}, " +
162 "Flow entries removed {}, " +
163 "Flow entries skipped {}"
164 , added
165 , removed
166 , skipped);
Brian O'Connore46492e2013-11-14 21:11:50 -0800167
Ray Milkey8e5170e2014-04-02 12:09:55 -0700168 return new SyncResult(added, removed, skipped);
169 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700170
Ray Milkey8e5170e2014-04-02 12:09:55 -0700171 /**
172 * Read GraphDB to get FlowEntries associated with a switch.
173 *
174 * @return set of FlowEntries
175 */
176 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
177 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700178
Ray Milkey8e5170e2014-04-02 12:09:55 -0700179 // TODO: fix when FlowSynchronizer is refactored
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700180 /*
Ray Milkey8e5170e2014-04-02 12:09:55 -0700181 for(IFlowEntry entry : swObj.getFlowEntries()) {
182 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
183 entries.add(fe);
184 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700185 */
Ray Milkey8e5170e2014-04-02 12:09:55 -0700186 return entries;
187 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700188
Ray Milkey8e5170e2014-04-02 12:09:55 -0700189 /**
190 * Read flow table from switch and derive FlowEntries from table.
191 *
192 * @return set of FlowEntries
193 */
194 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700195
Ray Milkey8e5170e2014-04-02 12:09:55 -0700196 int lengthU = 0;
197 OFMatch match = new OFMatch();
198 match.setWildcards(OFMatch.OFPFW_ALL);
Brian O'Connore46492e2013-11-14 21:11:50 -0800199
Ray Milkey8e5170e2014-04-02 12:09:55 -0700200 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
201 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
202 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
203 stat.setMatch(match);
204 List<OFStatistics> stats = new ArrayList<OFStatistics>();
205 stats.add(stat);
206 lengthU += stat.getLength();
207
208 OFStatisticsRequest req = new OFStatisticsRequest();
209 req.setStatisticType(OFStatisticsType.FLOW);
210 req.setStatistics(stats);
211 lengthU += req.getLengthU();
212 req.setLengthU(lengthU);
213
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700214 //List<OFStatistics> entries = null;
215 // XXX S when we fix stats, we fix this
216 /*try {
Ray Milkey8e5170e2014-04-02 12:09:55 -0700217 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
218 entries = dfuture.get();
219 } catch (IOException e) {
Yuta HIGUCHIe933a282014-07-13 21:02:29 -0700220 log.error("Error getting statistics", e);
Ray Milkey8e5170e2014-04-02 12:09:55 -0700221 return null;
222 } catch (InterruptedException e) {
Yuta HIGUCHIe933a282014-07-13 21:02:29 -0700223 log.error("Error getting statistics", e);
Ray Milkey8e5170e2014-04-02 12:09:55 -0700224 return null;
225 } catch (ExecutionException 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;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700228 }*/
Ray Milkey8e5170e2014-04-02 12:09:55 -0700229
230 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700231 /*
Ray Milkey8e5170e2014-04-02 12:09:55 -0700232 for (OFStatistics result : entries) {
233 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
234 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
235 results.add(fe);
236 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700237 */
Ray Milkey8e5170e2014-04-02 12:09:55 -0700238 return results;
239 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800240
Brian O'Connora8e49802013-10-30 20:49:59 -0700241 }
242
Naoki Shiotae3199732013-11-25 16:14:43 -0800243 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800244 * FlowEntryWrapper represents abstract FlowEntry which is embodied
245 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800246 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700247 static class FlowEntryWrapper {
Ray Milkey8e5170e2014-04-02 12:09:55 -0700248 FlowEntryId flowEntryId;
249 // TODO: fix when FlowSynchronizer is refactored
250 // IFlowEntry iFlowEntry;
251 OFFlowStatisticsReply statisticsReply;
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800252
Brian O'Connore46492e2013-11-14 21:11:50 -0800253
Ray Milkey8e5170e2014-04-02 12:09:55 -0700254 // TODO: fix when FlowSynchronizer is refactored
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700255 /*
Ray Milkey8e5170e2014-04-02 12:09:55 -0700256 public FlowEntryWrapper(IFlowEntry entry) {
257 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
258 iFlowEntry = entry;
259 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700260 */
Brian O'Connora8e49802013-10-30 20:49:59 -0700261
Ray Milkey8e5170e2014-04-02 12:09:55 -0700262 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
263 flowEntryId = new FlowEntryId(entry.getCookie());
264 statisticsReply = entry;
265 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700266
Ray Milkey8e5170e2014-04-02 12:09:55 -0700267 /**
268 * Install this FlowEntry to a switch via FlowPusher.
269 *
270 * @param sw Switch to which flow will be installed.
271 */
272 double dbTime, extractTime, pushTime;
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800273
Ray Milkey8e5170e2014-04-02 12:09:55 -0700274 public void addToSwitch(IOFSwitch sw) {
275 if (statisticsReply != null) {
276 log.error("Error adding existing flow entry {} to sw {}",
277 statisticsReply.getCookie(), sw.getId());
278 return;
Naoki Shiotaf74d5f32014-01-09 21:29:38 -0800279 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800280
Ray Milkey8e5170e2014-04-02 12:09:55 -0700281 double startDB = System.nanoTime();
282 // Get the Flow Entry state from the Network Graph
283 // TODO: fix when FlowSynchronizer is refactored
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700284 /*
Ray Milkey8e5170e2014-04-02 12:09:55 -0700285 if (iFlowEntry == null) {
286 try {
287 // TODO: fix when FlowSynchronizer is refactored
288 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
289 } catch (Exception e) {
290 log.error("Error finding flow entry {} in Network Graph",
291 flowEntryId);
292 return;
293 }
294 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700295 */
Ray Milkey8e5170e2014-04-02 12:09:55 -0700296 dbTime = System.nanoTime() - startDB;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800297
Ray Milkey8e5170e2014-04-02 12:09:55 -0700298 //
299 // TODO: The old FlowDatabaseOperation class is gone, so the code
300 //
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700301 /*
Ray Milkey8e5170e2014-04-02 12:09:55 -0700302 double startExtract = System.nanoTime();
303 FlowEntry flowEntry =
304 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
305 if (flowEntry == null) {
306 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
307 flowEntryId, sw.getId());
308 return;
309 }
310 extractTime = System.nanoTime() - startExtract;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800311
Ray Milkey8e5170e2014-04-02 12:09:55 -0700312 double startPush = System.nanoTime();
313 pusher.pushFlowEntry(sw, flowEntry, MsgPriority.HIGH);
314 pushTime = System.nanoTime() - startPush;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700315 */
Ray Milkey8e5170e2014-04-02 12:09:55 -0700316 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700317
Ray Milkey8e5170e2014-04-02 12:09:55 -0700318 /**
319 * Remove this FlowEntry from a switch via FlowPusher.
320 *
321 * @param sw Switch from which flow will be removed.
322 */
323 public void removeFromSwitch(IOFSwitch sw) {
324 if (statisticsReply == null) {
325 log.error("Error removing non-existent flow entry {} from sw {}",
326 flowEntryId, sw.getId());
327 return;
328 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800329
Ray Milkey8e5170e2014-04-02 12:09:55 -0700330 // Convert Statistics Reply to Flow Mod, then write it
331 OFFlowMod fm = new OFFlowMod();
332 fm.setCookie(statisticsReply.getCookie());
333 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
334 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
335 fm.setMatch(statisticsReply.getMatch());
336 fm.setPriority(statisticsReply.getPriority());
337 fm.setOutPort(OFPort.OFPP_NONE);
Brian O'Connora8e49802013-10-30 20:49:59 -0700338
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700339 // XXX BOC commented out pending FlowSync refactor
340 //pusher.add(sw, fm, MsgPriority.HIGH);
Ray Milkey8e5170e2014-04-02 12:09:55 -0700341 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700342
Ray Milkey8e5170e2014-04-02 12:09:55 -0700343 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700344 * Return the hash code of the Flow Entry ID.
Ray Milkey8e5170e2014-04-02 12:09:55 -0700345 */
346 @Override
347 public int hashCode() {
348 return flowEntryId.hashCode();
349 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800350
Ray Milkey8e5170e2014-04-02 12:09:55 -0700351 /**
352 * Returns true of the object is another Flow Entry ID with
353 * the same value; otherwise, returns false.
354 *
Sho SHIMIZUa1199fa2014-06-10 18:11:12 -0700355 * @param obj to compare
Ray Milkey8e5170e2014-04-02 12:09:55 -0700356 * @return true if the object has the same Flow Entry ID.
357 */
358 @Override
359 public boolean equals(Object obj) {
360 if (obj != null && obj.getClass() == this.getClass()) {
361 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
362 // TODO: we need to actually compare the match + actions
363 return this.flowEntryId.equals(entry.flowEntryId);
364 }
365 return false;
366 }
367
368 @Override
369 public String toString() {
370 return flowEntryId.toString();
371 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800372 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800373}