blob: 740e782b11d3bb8f8f5fb184ca7a3366c1b39fd6 [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;
Brian O'Connora8e49802013-10-30 20:49:59 -070027import net.onrc.onos.graph.GraphDBOperation;
28import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
29import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -080030import net.onrc.onos.ofcontroller.flowmanager.FlowDatabaseOperation;
Brian O'Connora8e49802013-10-30 20:49:59 -070031import net.onrc.onos.ofcontroller.util.Dpid;
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -080032import net.onrc.onos.ofcontroller.util.FlowEntry;
Brian O'Connora8e49802013-10-30 20:49:59 -070033import net.onrc.onos.ofcontroller.util.FlowEntryId;
34
Naoki Shiotae3199732013-11-25 16:14:43 -080035/**
Naoki Shiotab485d412013-11-26 12:04:19 -080036 * FlowSynchronizer is an implementation of FlowSyncService.
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -080037 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
38 * tables from switches and compare them with GraphDB to drop unnecessary
39 * flows and/or to install missing flows.
Naoki Shiotae3199732013-11-25 16:14:43 -080040 * @author Brian
41 *
42 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080043public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070044
Brian O'Connorea1efbe2013-11-25 22:57:43 -080045 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080046
Brian O'Connor8c166a72013-11-14 18:41:48 -080047 private GraphDBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080048 protected IFlowPusherService pusher;
Naoki Shiota2bdda572013-12-09 15:05:21 -080049 private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080050
51 public FlowSynchronizer() {
52 dbHandler = new GraphDBOperation("");
Naoki Shiota2bdda572013-12-09 15:05:21 -080053 switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>();
Brian O'Connore46492e2013-11-14 21:11:50 -080054 }
55
Brian O'Connorea1efbe2013-11-25 22:57:43 -080056 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -080057 public Future<SyncResult> synchronize(IOFSwitch sw) {
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080058 Synchronizer sync = new Synchronizer(sw);
Naoki Shiota2bdda572013-12-09 15:05:21 -080059 FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync);
60 switchThreads.put(sw, task);
61 task.run();
62 return task;
Brian O'Connore46492e2013-11-14 21:11:50 -080063 }
Brian O'Connorea1efbe2013-11-25 22:57:43 -080064
Brian O'Connore46492e2013-11-14 21:11:50 -080065 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080066 public void interrupt(IOFSwitch sw) {
Naoki Shiota2bdda572013-12-09 15:05:21 -080067 FutureTask<SyncResult> t = switchThreads.remove(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080068 if(t != null) {
Naoki Shiota2bdda572013-12-09 15:05:21 -080069 t.cancel(true);
Brian O'Connorea1efbe2013-11-25 22:57:43 -080070 }
Brian O'Connore46492e2013-11-14 21:11:50 -080071 }
72
Naoki Shiotab485d412013-11-26 12:04:19 -080073 /**
74 * Initialize Synchronizer.
75 * @param pusherService FlowPusherService used for sending messages.
76 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080077 public void init(IFlowPusherService pusherService) {
78 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.
83 * @author Brian
84 *
85 */
Naoki Shiota2bdda572013-12-09 15:05:21 -080086 protected class Synchronizer implements Callable<SyncResult> {
Brian O'Connora8e49802013-10-30 20:49:59 -070087 IOFSwitch sw;
88 ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080089
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080090 public Synchronizer(IOFSwitch sw) {
Brian O'Connora8e49802013-10-30 20:49:59 -070091 this.sw = sw;
92 Dpid dpid = new Dpid(sw.getId());
93 this.swObj = dbHandler.searchSwitch(dpid.toString());
94 }
Brian O'Connore46492e2013-11-14 21:11:50 -080095
Brian O'Connor321a5d32013-12-09 18:11:35 -080096 double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime;
Brian O'Connora8e49802013-10-30 20:49:59 -070097 @Override
Naoki Shiota2bdda572013-12-09 15:05:21 -080098 public SyncResult call() {
Brian O'Connorea1efbe2013-11-25 22:57:43 -080099 // TODO: stop adding other flow entries while synchronizing
100 //pusher.suspend(sw);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800101 long start = System.nanoTime();
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800102 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
Brian O'Connor321a5d32013-12-09 18:11:35 -0800103 long step1 = System.nanoTime();
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800104 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
Brian O'Connor321a5d32013-12-09 18:11:35 -0800105 long step2 = System.nanoTime();
Naoki Shiota2bdda572013-12-09 15:05:21 -0800106 SyncResult result = compare(graphEntries, switchEntries);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800107 long step3 = System.nanoTime();
108 graphIDTime = (step1 - start);
109 switchTime = (step2 - step1);
110 compareTime = (step3 - step2);
111 totalTime = (step3 - start);
112 outputTime();
Brian O'Connorea1efbe2013-11-25 22:57:43 -0800113 //pusher.resume(sw);
Naoki Shiota2bdda572013-12-09 15:05:21 -0800114
115 return result;
Brian O'Connora8e49802013-10-30 20:49:59 -0700116 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800117
118 private void outputTime() {
119 double div = Math.pow(10, 6); //convert nanoseconds to ms
120 graphIDTime /= div;
121 switchTime /= div;
122 compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
123 graphEntryTime /= div;
124 extractTime /= div;
125 pushTime /= div;
Brian O'Connor8f7f8582013-12-11 15:48:07 -0800126 totalTime /= div;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800127 log.debug("Sync time (ms):{},{},{},{},{},{},{}"
128 , graphIDTime
129 , switchTime
130 , compareTime
131 , graphEntryTime
132 , extractTime
133 , pushTime
134 , totalTime);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800135 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800136
Naoki Shiotae3199732013-11-25 16:14:43 -0800137 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800138 * Compare flows entries in GraphDB and switch to pick up necessary
139 * messages.
Naoki Shiotae3199732013-11-25 16:14:43 -0800140 * After picking up, picked messages are added to FlowPusher.
141 * @param graphEntries Flow entries in GraphDB.
142 * @param switchEntries Flow entries in switch.
143 */
Naoki Shiota2bdda572013-12-09 15:05:21 -0800144 private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800145 int added = 0, removed = 0, skipped = 0;
146 for(FlowEntryWrapper entry : switchEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800147 if(graphEntries.contains(entry)) {
148 graphEntries.remove(entry);
149 skipped++;
150 }
151 else {
152 // remove flow entry from the switch
153 entry.removeFromSwitch(sw);
154 removed++;
155 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700156 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800157 for(FlowEntryWrapper entry : graphEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800158 // add flow entry to switch
159 entry.addToSwitch(sw);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800160 graphEntryTime += entry.dbTime;
161 extractTime += entry.extractTime;
162 pushTime += entry.pushTime;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800163 added++;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800164 }
165 log.debug("Flow entries added {}, " +
166 "Flow entries removed {}, " +
167 "Flow entries skipped {}"
168 , added
169 , removed
170 , skipped );
171
Naoki Shiota2bdda572013-12-09 15:05:21 -0800172 return new SyncResult(added, removed, skipped);
Brian O'Connora8e49802013-10-30 20:49:59 -0700173 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800174
Naoki Shiotae3199732013-11-25 16:14:43 -0800175 /**
176 * Read GraphDB to get FlowEntries associated with a switch.
177 * @return set of FlowEntries
178 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800179 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
180 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700181 for(IFlowEntry entry : swObj.getFlowEntries()) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800182 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
183 entries.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700184 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800185 return entries;
Brian O'Connora8e49802013-10-30 20:49:59 -0700186 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800187
Naoki Shiotae3199732013-11-25 16:14:43 -0800188 /**
189 * Read flow table from switch and derive FlowEntries from table.
190 * @return set of FlowEntries
191 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800192 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700193
194 int lengthU = 0;
195 OFMatch match = new OFMatch();
196 match.setWildcards(OFMatch.OFPFW_ALL);
197
198 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
199 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
200 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
201 stat.setMatch(match);
202 List<OFStatistics> stats = new ArrayList<OFStatistics>();
203 stats.add(stat);
204 lengthU += stat.getLength();
205
206 OFStatisticsRequest req = new OFStatisticsRequest();
207 req.setStatisticType(OFStatisticsType.FLOW);
208 req.setStatistics(stats);
209 lengthU += req.getLengthU();
210 req.setLengthU(lengthU);
211
212 List<OFStatistics> entries = null;
213 try {
214 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
215 entries = dfuture.get();
216 } catch (IOException e) {
217 // TODO Auto-generated catch block
218 e.printStackTrace();
219 } catch (InterruptedException e) {
220 // TODO Auto-generated catch block
221 e.printStackTrace();
222 } catch (ExecutionException e) {
223 // TODO Auto-generated catch block
224 e.printStackTrace();
225 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800226
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800227 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700228 for(OFStatistics result : entries){
Brian O'Connora8e49802013-10-30 20:49:59 -0700229 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800230 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
231 results.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700232 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800233 return results;
Brian O'Connora8e49802013-10-30 20:49:59 -0700234 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800235
Brian O'Connora8e49802013-10-30 20:49:59 -0700236 }
237
Naoki Shiotae3199732013-11-25 16:14:43 -0800238 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800239 * FlowEntryWrapper represents abstract FlowEntry which is embodied
240 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800241 * @author Brian
242 *
243 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800244 class FlowEntryWrapper {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800245 FlowEntryId flowEntryId;
Brian O'Connore46492e2013-11-14 21:11:50 -0800246 OFFlowStatisticsReply statisticsReply;
247
248 public FlowEntryWrapper(IFlowEntry entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800249 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
Brian O'Connora8e49802013-10-30 20:49:59 -0700250 }
251
Brian O'Connore46492e2013-11-14 21:11:50 -0800252 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800253 flowEntryId = new FlowEntryId(entry.getCookie());
Brian O'Connore46492e2013-11-14 21:11:50 -0800254 statisticsReply = entry;
Brian O'Connore46492e2013-11-14 21:11:50 -0800255 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700256
Naoki Shiotae3199732013-11-25 16:14:43 -0800257 /**
258 * Install this FlowEntry to a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800259 * @param sw Switch to which flow will be installed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800260 */
Brian O'Connor321a5d32013-12-09 18:11:35 -0800261 double dbTime, extractTime, pushTime;
Brian O'Connore46492e2013-11-14 21:11:50 -0800262 public void addToSwitch(IOFSwitch sw) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800263 if (statisticsReply != null) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800264 log.error("Error adding existing flow entry {} to sw {}",
Brian O'Connore46492e2013-11-14 21:11:50 -0800265 statisticsReply.getCookie(), sw.getId());
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800266 return;
Brian O'Connore46492e2013-11-14 21:11:50 -0800267 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800268
Brian O'Connor321a5d32013-12-09 18:11:35 -0800269 double startDB = System.nanoTime();
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800270 // Get the Flow Entry state from the Network Graph
271 IFlowEntry iFlowEntry = null;
272 try {
273 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
274 } catch (Exception e) {
275 log.error("Error finding flow entry {} in Network Graph",
276 flowEntryId);
277 return;
278 }
279 if (iFlowEntry == null) {
280 log.error("Cannot add flow entry {} to sw {} : flow entry not found",
281 flowEntryId, sw.getId());
282 return;
283 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800284 dbTime = System.nanoTime() - startDB;
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800285
Brian O'Connor321a5d32013-12-09 18:11:35 -0800286 double startExtract = System.nanoTime();
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -0800287 FlowEntry flowEntry =
288 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
289 if (flowEntry == null) {
290 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
291 flowEntryId, sw.getId());
292 return;
293 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800294 extractTime = System.nanoTime() - startExtract;
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800295
Brian O'Connor321a5d32013-12-09 18:11:35 -0800296 double startPush = System.nanoTime();
Pavlin Radoslavovab3f8862013-12-04 18:35:53 -0800297 pusher.pushFlowEntry(sw, flowEntry);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800298 pushTime = System.nanoTime() - startPush;
Brian O'Connore46492e2013-11-14 21:11:50 -0800299 }
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800300
Naoki Shiotae3199732013-11-25 16:14:43 -0800301 /**
302 * Remove this FlowEntry from a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800303 * @param sw Switch from which flow will be removed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800304 */
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800305 public void removeFromSwitch(IOFSwitch sw) {
306 if (statisticsReply == null) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800307 log.error("Error removing non-existent flow entry {} from sw {}",
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800308 flowEntryId, sw.getId());
309 return;
310 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700311
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800312 // Convert Statistics Reply to Flow Mod, then write it
313 OFFlowMod fm = new OFFlowMod();
314 fm.setCookie(statisticsReply.getCookie());
315 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
316 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
317 fm.setMatch(statisticsReply.getMatch());
318 fm.setPriority(statisticsReply.getPriority());
319 fm.setOutPort(OFPort.OFPP_NONE);
320
321 pusher.add(sw, fm);
Brian O'Connore46492e2013-11-14 21:11:50 -0800322 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700323
Brian O'Connore46492e2013-11-14 21:11:50 -0800324 /**
325 * Return the hash code of the Flow Entry ID
326 */
327 @Override
328 public int hashCode() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800329 return flowEntryId.hashCode();
Brian O'Connore46492e2013-11-14 21:11:50 -0800330 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700331
Brian O'Connore46492e2013-11-14 21:11:50 -0800332 /**
333 * Returns true of the object is another Flow Entry ID with
334 * the same value; otherwise, returns false.
335 *
336 * @param Object to compare
Naoki Shiotab485d412013-11-26 12:04:19 -0800337 * @return true if the object has the same Flow Entry ID.
Brian O'Connore46492e2013-11-14 21:11:50 -0800338 */
339 @Override
340 public boolean equals(Object obj){
341 if(obj.getClass() == this.getClass()) {
342 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
343 // TODO: we need to actually compare the match + actions
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800344 return this.flowEntryId.equals(entry.flowEntryId);
Brian O'Connore46492e2013-11-14 21:11:50 -0800345 }
346 return false;
347 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800348
Brian O'Connore46492e2013-11-14 21:11:50 -0800349 @Override
350 public String toString() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800351 return flowEntryId.toString();
Brian O'Connore46492e2013-11-14 21:11:50 -0800352 }
353 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800354}