blob: b3c8e847bc1a365de72bd2dd5721efba21a92f99 [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;
Brian O'Connora8e49802013-10-30 20:49:59 -070028import net.onrc.onos.ofcontroller.util.Dpid;
Brian O'Connora8e49802013-10-30 20:49:59 -070029import net.onrc.onos.ofcontroller.util.FlowEntryId;
30
Naoki Shiotae3199732013-11-25 16:14:43 -080031/**
Naoki Shiotab485d412013-11-26 12:04:19 -080032 * FlowSynchronizer is an implementation of FlowSyncService.
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -080033 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
34 * tables from switches and compare them with GraphDB to drop unnecessary
35 * flows and/or to install missing flows.
Naoki Shiotae3199732013-11-25 16:14:43 -080036 * @author Brian
37 *
38 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080039public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070040
Brian O'Connorea1efbe2013-11-25 22:57:43 -080041 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080042
Brian O'Connor8c166a72013-11-14 18:41:48 -080043 private GraphDBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080044 protected IFlowPusherService pusher;
45 private Map<IOFSwitch, Thread> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080046
47 public FlowSynchronizer() {
48 dbHandler = new GraphDBOperation("");
Brian O'Connorea1efbe2013-11-25 22:57:43 -080049 switchThreads = new HashMap<IOFSwitch, Thread>();
Brian O'Connore46492e2013-11-14 21:11:50 -080050 }
51
Brian O'Connorea1efbe2013-11-25 22:57:43 -080052 @Override
Brian O'Connore46492e2013-11-14 21:11:50 -080053 public void synchronize(IOFSwitch sw) {
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080054 Synchronizer sync = new Synchronizer(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080055 Thread t = new Thread(sync);
Brian O'Connorea1efbe2013-11-25 22:57:43 -080056 switchThreads.put(sw, t);
Brian O'Connore46492e2013-11-14 21:11:50 -080057 t.start();
Brian O'Connore46492e2013-11-14 21:11:50 -080058 }
Brian O'Connorea1efbe2013-11-25 22:57:43 -080059
Brian O'Connore46492e2013-11-14 21:11:50 -080060 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080061 public void interrupt(IOFSwitch sw) {
62 Thread t = switchThreads.remove(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080063 if(t != null) {
64 t.interrupt();
Brian O'Connorea1efbe2013-11-25 22:57:43 -080065 }
Brian O'Connore46492e2013-11-14 21:11:50 -080066 }
67
Naoki Shiotab485d412013-11-26 12:04:19 -080068 /**
69 * Initialize Synchronizer.
70 * @param pusherService FlowPusherService used for sending messages.
71 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080072 public void init(IFlowPusherService pusherService) {
73 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080074 }
75
Naoki Shiotae3199732013-11-25 16:14:43 -080076 /**
77 * Synchronizer represents main thread of synchronization.
78 * @author Brian
79 *
80 */
81 protected class Synchronizer implements Runnable {
Brian O'Connora8e49802013-10-30 20:49:59 -070082 IOFSwitch sw;
83 ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080084
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080085 public Synchronizer(IOFSwitch sw) {
Brian O'Connora8e49802013-10-30 20:49:59 -070086 this.sw = sw;
87 Dpid dpid = new Dpid(sw.getId());
88 this.swObj = dbHandler.searchSwitch(dpid.toString());
89 }
Brian O'Connore46492e2013-11-14 21:11:50 -080090
Brian O'Connora8e49802013-10-30 20:49:59 -070091 @Override
92 public void run() {
Brian O'Connorea1efbe2013-11-25 22:57:43 -080093 // TODO: stop adding other flow entries while synchronizing
94 //pusher.suspend(sw);
Brian O'Connor0d6ba512013-11-05 15:17:44 -080095 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
96 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
Brian O'Connora8e49802013-10-30 20:49:59 -070097 compare(graphEntries, switchEntries);
Brian O'Connorea1efbe2013-11-25 22:57:43 -080098 //pusher.resume(sw);
Brian O'Connora8e49802013-10-30 20:49:59 -070099 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800100
Naoki Shiotae3199732013-11-25 16:14:43 -0800101 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800102 * Compare flows entries in GraphDB and switch to pick up necessary
103 * messages.
Naoki Shiotae3199732013-11-25 16:14:43 -0800104 * After picking up, picked messages are added to FlowPusher.
105 * @param graphEntries Flow entries in GraphDB.
106 * @param switchEntries Flow entries in switch.
107 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800108 private void compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800109 int added = 0, removed = 0, skipped = 0;
110 for(FlowEntryWrapper entry : switchEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800111 if(graphEntries.contains(entry)) {
112 graphEntries.remove(entry);
113 skipped++;
114 }
115 else {
116 // remove flow entry from the switch
117 entry.removeFromSwitch(sw);
118 removed++;
119 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700120 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800121 for(FlowEntryWrapper entry : graphEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800122 // add flow entry to switch
123 entry.addToSwitch(sw);
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800124 added++;
125 }
126 log.debug("Flow entries added "+ added + ", " +
127 "Flow entries removed "+ removed + ", " +
128 "Flow entries skipped " + skipped);
Brian O'Connora8e49802013-10-30 20:49:59 -0700129 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800130
Naoki Shiotae3199732013-11-25 16:14:43 -0800131 /**
132 * Read GraphDB to get FlowEntries associated with a switch.
133 * @return set of FlowEntries
134 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800135 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
136 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700137 for(IFlowEntry entry : swObj.getFlowEntries()) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800138 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
139 entries.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700140 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800141 return entries;
Brian O'Connora8e49802013-10-30 20:49:59 -0700142 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800143
Naoki Shiotae3199732013-11-25 16:14:43 -0800144 /**
145 * Read flow table from switch and derive FlowEntries from table.
146 * @return set of FlowEntries
147 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800148 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700149
150 int lengthU = 0;
151 OFMatch match = new OFMatch();
152 match.setWildcards(OFMatch.OFPFW_ALL);
153
154 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
155 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
156 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
157 stat.setMatch(match);
158 List<OFStatistics> stats = new ArrayList<OFStatistics>();
159 stats.add(stat);
160 lengthU += stat.getLength();
161
162 OFStatisticsRequest req = new OFStatisticsRequest();
163 req.setStatisticType(OFStatisticsType.FLOW);
164 req.setStatistics(stats);
165 lengthU += req.getLengthU();
166 req.setLengthU(lengthU);
167
168 List<OFStatistics> entries = null;
169 try {
170 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
171 entries = dfuture.get();
172 } catch (IOException e) {
173 // TODO Auto-generated catch block
174 e.printStackTrace();
175 } catch (InterruptedException e) {
176 // TODO Auto-generated catch block
177 e.printStackTrace();
178 } catch (ExecutionException e) {
179 // TODO Auto-generated catch block
180 e.printStackTrace();
181 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800182
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800183 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700184 for(OFStatistics result : entries){
Brian O'Connora8e49802013-10-30 20:49:59 -0700185 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800186 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
187 results.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700188 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800189 return results;
Brian O'Connora8e49802013-10-30 20:49:59 -0700190 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800191
Brian O'Connora8e49802013-10-30 20:49:59 -0700192 }
193
Naoki Shiotae3199732013-11-25 16:14:43 -0800194 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800195 * FlowEntryWrapper represents abstract FlowEntry which is embodied
196 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800197 * @author Brian
198 *
199 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800200 class FlowEntryWrapper {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800201 FlowEntryId flowEntryId;
Brian O'Connore46492e2013-11-14 21:11:50 -0800202 OFFlowStatisticsReply statisticsReply;
203
204 public FlowEntryWrapper(IFlowEntry entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800205 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
Brian O'Connora8e49802013-10-30 20:49:59 -0700206 }
207
Brian O'Connore46492e2013-11-14 21:11:50 -0800208 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800209 flowEntryId = new FlowEntryId(entry.getCookie());
Brian O'Connore46492e2013-11-14 21:11:50 -0800210 statisticsReply = entry;
Brian O'Connore46492e2013-11-14 21:11:50 -0800211 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700212
Naoki Shiotae3199732013-11-25 16:14:43 -0800213 /**
214 * Install this FlowEntry to a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800215 * @param sw Switch to which flow will be installed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800216 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800217 public void addToSwitch(IOFSwitch sw) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800218 if (statisticsReply != null) {
219 log.error("Error adding existing flow entry {} to sw {}",
Brian O'Connore46492e2013-11-14 21:11:50 -0800220 statisticsReply.getCookie(), sw.getId());
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800221 return;
Brian O'Connore46492e2013-11-14 21:11:50 -0800222 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800223
224 // Get the Flow Entry state from the Network Graph
225 IFlowEntry iFlowEntry = null;
226 try {
227 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
228 } catch (Exception e) {
229 log.error("Error finding flow entry {} in Network Graph",
230 flowEntryId);
231 return;
232 }
233 if (iFlowEntry == null) {
234 log.error("Cannot add flow entry {} to sw {} : flow entry not found",
235 flowEntryId, sw.getId());
236 return;
237 }
238
239 pusher.add(sw, iFlowEntry.getFlow(), iFlowEntry);
Brian O'Connore46492e2013-11-14 21:11:50 -0800240 }
241
Naoki Shiotae3199732013-11-25 16:14:43 -0800242 /**
243 * Remove this FlowEntry from a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800244 * @param sw Switch from which flow will be removed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800245 */
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800246 public void removeFromSwitch(IOFSwitch sw) {
247 if (statisticsReply == null) {
248 log.error("Error removing non-existent flow entry {} from sw {}",
249 flowEntryId, sw.getId());
250 return;
251 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700252
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800253 // Convert Statistics Reply to Flow Mod, then write it
254 OFFlowMod fm = new OFFlowMod();
255 fm.setCookie(statisticsReply.getCookie());
256 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
257 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
258 fm.setMatch(statisticsReply.getMatch());
259 fm.setPriority(statisticsReply.getPriority());
260 fm.setOutPort(OFPort.OFPP_NONE);
261
262 pusher.add(sw, fm);
Brian O'Connore46492e2013-11-14 21:11:50 -0800263 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700264
Brian O'Connore46492e2013-11-14 21:11:50 -0800265 /**
266 * Return the hash code of the Flow Entry ID
267 */
268 @Override
269 public int hashCode() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800270 return flowEntryId.hashCode();
Brian O'Connore46492e2013-11-14 21:11:50 -0800271 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700272
Brian O'Connore46492e2013-11-14 21:11:50 -0800273 /**
274 * Returns true of the object is another Flow Entry ID with
275 * the same value; otherwise, returns false.
276 *
277 * @param Object to compare
Naoki Shiotab485d412013-11-26 12:04:19 -0800278 * @return true if the object has the same Flow Entry ID.
Brian O'Connore46492e2013-11-14 21:11:50 -0800279 */
280 @Override
281 public boolean equals(Object obj){
282 if(obj.getClass() == this.getClass()) {
283 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
284 // TODO: we need to actually compare the match + actions
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800285 return this.flowEntryId.equals(entry.flowEntryId);
Brian O'Connore46492e2013-11-14 21:11:50 -0800286 }
287 return false;
288 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800289
Brian O'Connore46492e2013-11-14 21:11:50 -0800290 @Override
291 public String toString() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800292 return flowEntryId.toString();
Brian O'Connore46492e2013-11-14 21:11:50 -0800293 }
294 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800295}