blob: 0d6b0e8d1f92ce614d1e8ffd0525fe4d9776ecbd [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.
33 * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow tables from
Naoki Shiotae3199732013-11-25 16:14:43 -080034 * switches and compare them with GraphDB to drop unnecessary flows and/or to install
Naoki Shiotab485d412013-11-26 12:04:19 -080035 * 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 /**
102 * Compare flows entries in GraphDB and switch to pick up necessary messages.
103 * After picking up, picked messages are added to FlowPusher.
104 * @param graphEntries Flow entries in GraphDB.
105 * @param switchEntries Flow entries in switch.
106 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800107 private void compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800108 int added = 0, removed = 0, skipped = 0;
109 for(FlowEntryWrapper entry : switchEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800110 if(graphEntries.contains(entry)) {
111 graphEntries.remove(entry);
112 skipped++;
113 }
114 else {
115 // remove flow entry from the switch
116 entry.removeFromSwitch(sw);
117 removed++;
118 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700119 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800120 for(FlowEntryWrapper entry : graphEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800121 // add flow entry to switch
122 entry.addToSwitch(sw);
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800123 added++;
124 }
125 log.debug("Flow entries added "+ added + ", " +
126 "Flow entries removed "+ removed + ", " +
127 "Flow entries skipped " + skipped);
Brian O'Connora8e49802013-10-30 20:49:59 -0700128 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800129
Naoki Shiotae3199732013-11-25 16:14:43 -0800130 /**
131 * Read GraphDB to get FlowEntries associated with a switch.
132 * @return set of FlowEntries
133 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800134 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
135 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700136 for(IFlowEntry entry : swObj.getFlowEntries()) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800137 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
138 entries.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700139 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800140 return entries;
Brian O'Connora8e49802013-10-30 20:49:59 -0700141 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800142
Naoki Shiotae3199732013-11-25 16:14:43 -0800143 /**
144 * Read flow table from switch and derive FlowEntries from table.
145 * @return set of FlowEntries
146 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800147 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700148
149 int lengthU = 0;
150 OFMatch match = new OFMatch();
151 match.setWildcards(OFMatch.OFPFW_ALL);
152
153 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
154 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
155 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
156 stat.setMatch(match);
157 List<OFStatistics> stats = new ArrayList<OFStatistics>();
158 stats.add(stat);
159 lengthU += stat.getLength();
160
161 OFStatisticsRequest req = new OFStatisticsRequest();
162 req.setStatisticType(OFStatisticsType.FLOW);
163 req.setStatistics(stats);
164 lengthU += req.getLengthU();
165 req.setLengthU(lengthU);
166
167 List<OFStatistics> entries = null;
168 try {
169 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
170 entries = dfuture.get();
171 } catch (IOException e) {
172 // TODO Auto-generated catch block
173 e.printStackTrace();
174 } catch (InterruptedException e) {
175 // TODO Auto-generated catch block
176 e.printStackTrace();
177 } catch (ExecutionException e) {
178 // TODO Auto-generated catch block
179 e.printStackTrace();
180 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800181
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800182 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700183 for(OFStatistics result : entries){
Brian O'Connora8e49802013-10-30 20:49:59 -0700184 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800185 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
186 results.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700187 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800188 return results;
Brian O'Connora8e49802013-10-30 20:49:59 -0700189 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800190
Brian O'Connora8e49802013-10-30 20:49:59 -0700191 }
192
Naoki Shiotae3199732013-11-25 16:14:43 -0800193 /**
194 * FlowEntryWrapper represents abstract FlowEntry which is embodied by IFlowEntry
195 * (from GraphDB) or OFFlowStatisticsReply (from switch).
196 * @author Brian
197 *
198 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800199 class FlowEntryWrapper {
200 FlowEntryId id;
201 IFlowEntry iflowEntry;
202 OFFlowStatisticsReply statisticsReply;
203
204 public FlowEntryWrapper(IFlowEntry entry) {
205 iflowEntry = entry;
206 id = new FlowEntryId(entry.getFlowEntryId());
Brian O'Connora8e49802013-10-30 20:49:59 -0700207 }
208
Brian O'Connore46492e2013-11-14 21:11:50 -0800209 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
210 statisticsReply = entry;
211 id = new FlowEntryId(entry.getCookie());
212 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700213
Naoki Shiotae3199732013-11-25 16:14:43 -0800214 /**
215 * Install this FlowEntry to a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800216 * @param sw Switch to which flow will be installed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800217 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800218 public void addToSwitch(IOFSwitch sw) {
219 if(iflowEntry != null) {
220 pusher.add(sw, iflowEntry.getFlow(), iflowEntry);
221 }
222 else if(statisticsReply != null) {
223 log.error("Adding existing flow entry {} to sw {}",
224 statisticsReply.getCookie(), sw.getId());
225 }
226 }
227
Naoki Shiotae3199732013-11-25 16:14:43 -0800228 /**
229 * Remove this FlowEntry from a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800230 * @param sw Switch from which flow will be removed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800231 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800232 public void removeFromSwitch(IOFSwitch sw){
233 if(iflowEntry != null) {
234 log.error("Removing non-existent flow entry {} from sw {}",
235 iflowEntry.getFlowEntryId(), sw.getId());
Brian O'Connora8e49802013-10-30 20:49:59 -0700236
Brian O'Connore46492e2013-11-14 21:11:50 -0800237 }
238 else if(statisticsReply != null) {
239 // Convert Statistics Reply to Flow Mod, then write it
240 OFFlowMod fm = new OFFlowMod();
241 fm.setCookie(statisticsReply.getCookie());
242 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
243 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
244 fm.setMatch(statisticsReply.getMatch());
245 fm.setPriority(statisticsReply.getPriority());
246 fm.setOutPort(OFPort.OFPP_NONE);
247 pusher.add(sw, fm);
248 }
249 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700250
Brian O'Connore46492e2013-11-14 21:11:50 -0800251 /**
252 * Return the hash code of the Flow Entry ID
253 */
254 @Override
255 public int hashCode() {
256 return id.hashCode();
257 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700258
Brian O'Connore46492e2013-11-14 21:11:50 -0800259 /**
260 * Returns true of the object is another Flow Entry ID with
261 * the same value; otherwise, returns false.
262 *
263 * @param Object to compare
Naoki Shiotab485d412013-11-26 12:04:19 -0800264 * @return true if the object has the same Flow Entry ID.
Brian O'Connore46492e2013-11-14 21:11:50 -0800265 */
266 @Override
267 public boolean equals(Object obj){
268 if(obj.getClass() == this.getClass()) {
269 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
270 // TODO: we need to actually compare the match + actions
271 return this.id.equals(entry.id);
272 }
273 return false;
274 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800275
Brian O'Connore46492e2013-11-14 21:11:50 -0800276 @Override
277 public String toString() {
278 return id.toString();
279 }
280 }
Brian O'Connorea1efbe2013-11-25 22:57:43 -0800281
282
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800283}
284
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800285