blob: 233212d3357b894677d047005542b1891860c9d0 [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;
10import java.util.concurrent.ExecutionException;
Brian O'Connora8e49802013-10-30 20:49:59 -070011import java.util.concurrent.Future;
12
Brian O'Connor0d6ba512013-11-05 15:17:44 -080013import org.openflow.protocol.OFFlowMod;
Brian O'Connora8e49802013-10-30 20:49:59 -070014import org.openflow.protocol.OFMatch;
Brian O'Connor0d6ba512013-11-05 15:17:44 -080015import org.openflow.protocol.OFPort;
Brian O'Connora8e49802013-10-30 20:49:59 -070016import org.openflow.protocol.OFStatisticsRequest;
17import org.openflow.protocol.statistics.OFFlowStatisticsReply;
18import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
19import org.openflow.protocol.statistics.OFStatistics;
20import org.openflow.protocol.statistics.OFStatisticsType;
21import org.slf4j.Logger;
22import org.slf4j.LoggerFactory;
23
Brian O'Connora8e49802013-10-30 20:49:59 -070024import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connora8e49802013-10-30 20:49:59 -070025import net.onrc.onos.graph.GraphDBOperation;
26import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
27import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -080028import net.onrc.onos.ofcontroller.flowmanager.FlowDatabaseOperation;
Brian O'Connora8e49802013-10-30 20:49:59 -070029import net.onrc.onos.ofcontroller.util.Dpid;
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -080030import net.onrc.onos.ofcontroller.util.FlowEntry;
Brian O'Connora8e49802013-10-30 20:49:59 -070031import net.onrc.onos.ofcontroller.util.FlowEntryId;
32
Naoki Shiotae3199732013-11-25 16:14:43 -080033/**
Naoki Shiotab485d412013-11-26 12:04:19 -080034 * FlowSynchronizer is an implementation of FlowSyncService.
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -080035 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
36 * tables from switches and compare them with GraphDB to drop unnecessary
37 * flows and/or to install missing flows.
Naoki Shiotae3199732013-11-25 16:14:43 -080038 * @author Brian
39 *
40 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080041public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070042
Brian O'Connorea1efbe2013-11-25 22:57:43 -080043 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080044
Brian O'Connor8c166a72013-11-14 18:41:48 -080045 private GraphDBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080046 protected IFlowPusherService pusher;
47 private Map<IOFSwitch, Thread> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080048
49 public FlowSynchronizer() {
50 dbHandler = new GraphDBOperation("");
Brian O'Connorea1efbe2013-11-25 22:57:43 -080051 switchThreads = new HashMap<IOFSwitch, Thread>();
Brian O'Connore46492e2013-11-14 21:11:50 -080052 }
53
Brian O'Connorea1efbe2013-11-25 22:57:43 -080054 @Override
Brian O'Connore46492e2013-11-14 21:11:50 -080055 public void synchronize(IOFSwitch sw) {
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080056 Synchronizer sync = new Synchronizer(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080057 Thread t = new Thread(sync);
Brian O'Connorea1efbe2013-11-25 22:57:43 -080058 switchThreads.put(sw, t);
Brian O'Connore46492e2013-11-14 21:11:50 -080059 t.start();
Brian O'Connore46492e2013-11-14 21:11:50 -080060 }
Brian O'Connorea1efbe2013-11-25 22:57:43 -080061
Brian O'Connore46492e2013-11-14 21:11:50 -080062 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080063 public void interrupt(IOFSwitch sw) {
64 Thread t = switchThreads.remove(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080065 if(t != null) {
66 t.interrupt();
Brian O'Connorea1efbe2013-11-25 22:57:43 -080067 }
Brian O'Connore46492e2013-11-14 21:11:50 -080068 }
69
Naoki Shiotab485d412013-11-26 12:04:19 -080070 /**
71 * Initialize Synchronizer.
72 * @param pusherService FlowPusherService used for sending messages.
73 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080074 public void init(IFlowPusherService pusherService) {
75 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080076 }
77
Naoki Shiotae3199732013-11-25 16:14:43 -080078 /**
79 * Synchronizer represents main thread of synchronization.
80 * @author Brian
81 *
82 */
83 protected class Synchronizer implements Runnable {
Brian O'Connora8e49802013-10-30 20:49:59 -070084 IOFSwitch sw;
85 ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080086
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080087 public Synchronizer(IOFSwitch sw) {
Brian O'Connora8e49802013-10-30 20:49:59 -070088 this.sw = sw;
89 Dpid dpid = new Dpid(sw.getId());
90 this.swObj = dbHandler.searchSwitch(dpid.toString());
91 }
Brian O'Connore46492e2013-11-14 21:11:50 -080092
Brian O'Connor321a5d32013-12-09 18:11:35 -080093 double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime;
Brian O'Connora8e49802013-10-30 20:49:59 -070094 @Override
95 public void run() {
Brian O'Connorea1efbe2013-11-25 22:57:43 -080096 // TODO: stop adding other flow entries while synchronizing
97 //pusher.suspend(sw);
Brian O'Connor321a5d32013-12-09 18:11:35 -080098 long start = System.nanoTime();
Brian O'Connor0d6ba512013-11-05 15:17:44 -080099 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
Brian O'Connor321a5d32013-12-09 18:11:35 -0800100 long step1 = System.nanoTime();
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800101 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
Brian O'Connor321a5d32013-12-09 18:11:35 -0800102 long step2 = System.nanoTime();
Brian O'Connora8e49802013-10-30 20:49:59 -0700103 compare(graphEntries, switchEntries);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800104 long step3 = System.nanoTime();
105 graphIDTime = (step1 - start);
106 switchTime = (step2 - step1);
107 compareTime = (step3 - step2);
108 totalTime = (step3 - start);
109 outputTime();
Brian O'Connorea1efbe2013-11-25 22:57:43 -0800110 //pusher.resume(sw);
Brian O'Connora8e49802013-10-30 20:49:59 -0700111 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800112
113 private void outputTime() {
114 double div = Math.pow(10, 6); //convert nanoseconds to ms
115 graphIDTime /= div;
116 switchTime /= div;
117 compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
118 graphEntryTime /= div;
119 extractTime /= div;
120 pushTime /= div;
121 log.debug("Sync time (ms):" +
122 graphIDTime + "," +
123 switchTime + "," +
124 compareTime + "," +
125 graphEntryTime + "," +
126 extractTime + "," +
127 pushTime + "," +
128 totalTime);
129 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800130
Naoki Shiotae3199732013-11-25 16:14:43 -0800131 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800132 * Compare flows entries in GraphDB and switch to pick up necessary
133 * messages.
Naoki Shiotae3199732013-11-25 16:14:43 -0800134 * After picking up, picked messages are added to FlowPusher.
135 * @param graphEntries Flow entries in GraphDB.
136 * @param switchEntries Flow entries in switch.
137 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800138 private void compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800139 int added = 0, removed = 0, skipped = 0;
140 for(FlowEntryWrapper entry : switchEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800141 if(graphEntries.contains(entry)) {
142 graphEntries.remove(entry);
143 skipped++;
144 }
145 else {
146 // remove flow entry from the switch
147 entry.removeFromSwitch(sw);
148 removed++;
149 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700150 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800151 for(FlowEntryWrapper entry : graphEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800152 // add flow entry to switch
153 entry.addToSwitch(sw);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800154 graphEntryTime += entry.dbTime;
155 extractTime += entry.extractTime;
156 pushTime += entry.pushTime;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800157 added++;
158 }
159 log.debug("Flow entries added "+ added + ", " +
160 "Flow entries removed "+ removed + ", " +
161 "Flow entries skipped " + skipped);
Brian O'Connora8e49802013-10-30 20:49:59 -0700162 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800163
Naoki Shiotae3199732013-11-25 16:14:43 -0800164 /**
165 * Read GraphDB to get FlowEntries associated with a switch.
166 * @return set of FlowEntries
167 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800168 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
169 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700170 for(IFlowEntry entry : swObj.getFlowEntries()) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800171 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
172 entries.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700173 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800174 return entries;
Brian O'Connora8e49802013-10-30 20:49:59 -0700175 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800176
Naoki Shiotae3199732013-11-25 16:14:43 -0800177 /**
178 * Read flow table from switch and derive FlowEntries from table.
179 * @return set of FlowEntries
180 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800181 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700182
183 int lengthU = 0;
184 OFMatch match = new OFMatch();
185 match.setWildcards(OFMatch.OFPFW_ALL);
186
187 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
188 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
189 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
190 stat.setMatch(match);
191 List<OFStatistics> stats = new ArrayList<OFStatistics>();
192 stats.add(stat);
193 lengthU += stat.getLength();
194
195 OFStatisticsRequest req = new OFStatisticsRequest();
196 req.setStatisticType(OFStatisticsType.FLOW);
197 req.setStatistics(stats);
198 lengthU += req.getLengthU();
199 req.setLengthU(lengthU);
200
201 List<OFStatistics> entries = null;
202 try {
203 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
204 entries = dfuture.get();
205 } catch (IOException e) {
206 // TODO Auto-generated catch block
207 e.printStackTrace();
208 } catch (InterruptedException e) {
209 // TODO Auto-generated catch block
210 e.printStackTrace();
211 } catch (ExecutionException e) {
212 // TODO Auto-generated catch block
213 e.printStackTrace();
214 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800215
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800216 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700217 for(OFStatistics result : entries){
Brian O'Connora8e49802013-10-30 20:49:59 -0700218 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800219 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
220 results.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700221 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800222 return results;
Brian O'Connora8e49802013-10-30 20:49:59 -0700223 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800224
Brian O'Connora8e49802013-10-30 20:49:59 -0700225 }
226
Naoki Shiotae3199732013-11-25 16:14:43 -0800227 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800228 * FlowEntryWrapper represents abstract FlowEntry which is embodied
229 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800230 * @author Brian
231 *
232 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800233 class FlowEntryWrapper {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800234 FlowEntryId flowEntryId;
Brian O'Connore46492e2013-11-14 21:11:50 -0800235 OFFlowStatisticsReply statisticsReply;
236
237 public FlowEntryWrapper(IFlowEntry entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800238 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
Brian O'Connora8e49802013-10-30 20:49:59 -0700239 }
240
Brian O'Connore46492e2013-11-14 21:11:50 -0800241 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800242 flowEntryId = new FlowEntryId(entry.getCookie());
Brian O'Connore46492e2013-11-14 21:11:50 -0800243 statisticsReply = entry;
Brian O'Connore46492e2013-11-14 21:11:50 -0800244 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700245
Naoki Shiotae3199732013-11-25 16:14:43 -0800246 /**
247 * Install this FlowEntry to a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800248 * @param sw Switch to which flow will be installed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800249 */
Brian O'Connor321a5d32013-12-09 18:11:35 -0800250 double dbTime, extractTime, pushTime;
Brian O'Connore46492e2013-11-14 21:11:50 -0800251 public void addToSwitch(IOFSwitch sw) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800252 if (statisticsReply != null) {
253 log.error("Error adding existing flow entry {} to sw {}",
Brian O'Connore46492e2013-11-14 21:11:50 -0800254 statisticsReply.getCookie(), sw.getId());
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800255 return;
Brian O'Connore46492e2013-11-14 21:11:50 -0800256 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800257
Brian O'Connor321a5d32013-12-09 18:11:35 -0800258 double startDB = System.nanoTime();
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800259 // Get the Flow Entry state from the Network Graph
260 IFlowEntry iFlowEntry = null;
261 try {
262 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
263 } catch (Exception e) {
264 log.error("Error finding flow entry {} in Network Graph",
265 flowEntryId);
266 return;
267 }
268 if (iFlowEntry == null) {
269 log.error("Cannot add flow entry {} to sw {} : flow entry not found",
270 flowEntryId, sw.getId());
271 return;
272 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800273 dbTime = System.nanoTime() - startDB;
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800274
Brian O'Connor321a5d32013-12-09 18:11:35 -0800275 double startExtract = System.nanoTime();
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -0800276 FlowEntry flowEntry =
277 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
278 if (flowEntry == null) {
279 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
280 flowEntryId, sw.getId());
281 return;
282 }
Brian O'Connor321a5d32013-12-09 18:11:35 -0800283 extractTime = System.nanoTime() - startExtract;
284
285 double startPush = System.nanoTime();
Pavlin Radoslavovab3f8862013-12-04 18:35:53 -0800286 pusher.pushFlowEntry(sw, flowEntry);
Brian O'Connor321a5d32013-12-09 18:11:35 -0800287 pushTime = System.nanoTime() - startPush;
Brian O'Connore46492e2013-11-14 21:11:50 -0800288 }
289
Naoki Shiotae3199732013-11-25 16:14:43 -0800290 /**
291 * Remove this FlowEntry from a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800292 * @param sw Switch from which flow will be removed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800293 */
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800294 public void removeFromSwitch(IOFSwitch sw) {
295 if (statisticsReply == null) {
296 log.error("Error removing non-existent flow entry {} from sw {}",
297 flowEntryId, sw.getId());
298 return;
299 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700300
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800301 // Convert Statistics Reply to Flow Mod, then write it
302 OFFlowMod fm = new OFFlowMod();
303 fm.setCookie(statisticsReply.getCookie());
304 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
305 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
306 fm.setMatch(statisticsReply.getMatch());
307 fm.setPriority(statisticsReply.getPriority());
308 fm.setOutPort(OFPort.OFPP_NONE);
309
310 pusher.add(sw, fm);
Brian O'Connore46492e2013-11-14 21:11:50 -0800311 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700312
Brian O'Connore46492e2013-11-14 21:11:50 -0800313 /**
314 * Return the hash code of the Flow Entry ID
315 */
316 @Override
317 public int hashCode() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800318 return flowEntryId.hashCode();
Brian O'Connore46492e2013-11-14 21:11:50 -0800319 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700320
Brian O'Connore46492e2013-11-14 21:11:50 -0800321 /**
322 * Returns true of the object is another Flow Entry ID with
323 * the same value; otherwise, returns false.
324 *
325 * @param Object to compare
Naoki Shiotab485d412013-11-26 12:04:19 -0800326 * @return true if the object has the same Flow Entry ID.
Brian O'Connore46492e2013-11-14 21:11:50 -0800327 */
328 @Override
329 public boolean equals(Object obj){
330 if(obj.getClass() == this.getClass()) {
331 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
332 // TODO: we need to actually compare the match + actions
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800333 return this.flowEntryId.equals(entry.flowEntryId);
Brian O'Connore46492e2013-11-14 21:11:50 -0800334 }
335 return false;
336 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800337
Brian O'Connore46492e2013-11-14 21:11:50 -0800338 @Override
339 public String toString() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800340 return flowEntryId.toString();
Brian O'Connore46492e2013-11-14 21:11:50 -0800341 }
342 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800343}