blob: 3250eb4a56db439de46f883485c286bb51371eec [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/**
32 * FlowSynchronizer is a sub-module of FlowProgrammer to keep switches' flow table
33 * synchronized with GraphDB. FlowSynchronizer periodically read flow tables from
34 * switches and compare them with GraphDB to drop unnecessary flows and/or to install
35 * missing flows. FlowSynchronizer also watch the event of addition/deletion of switches
36 * and start synchronization.
37 * @author Brian
38 *
39 */
Brian O'Connorea1efbe2013-11-25 22:57:43 -080040public class FlowSynchronizer implements IFlowSyncService {
Brian O'Connora8e49802013-10-30 20:49:59 -070041
Brian O'Connorea1efbe2013-11-25 22:57:43 -080042 private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
Brian O'Connore46492e2013-11-14 21:11:50 -080043
Brian O'Connor8c166a72013-11-14 18:41:48 -080044 private GraphDBOperation dbHandler;
Brian O'Connorea1efbe2013-11-25 22:57:43 -080045 protected IFlowPusherService pusher;
46 private Map<IOFSwitch, Thread> switchThreads;
Brian O'Connore46492e2013-11-14 21:11:50 -080047
48 public FlowSynchronizer() {
49 dbHandler = new GraphDBOperation("");
Brian O'Connorea1efbe2013-11-25 22:57:43 -080050 switchThreads = new HashMap<IOFSwitch, Thread>();
Brian O'Connore46492e2013-11-14 21:11:50 -080051 }
52
Brian O'Connorea1efbe2013-11-25 22:57:43 -080053 @Override
Brian O'Connore46492e2013-11-14 21:11:50 -080054 public void synchronize(IOFSwitch sw) {
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080055 Synchronizer sync = new Synchronizer(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080056 Thread t = new Thread(sync);
Brian O'Connorea1efbe2013-11-25 22:57:43 -080057 switchThreads.put(sw, t);
Brian O'Connore46492e2013-11-14 21:11:50 -080058 t.start();
Brian O'Connore46492e2013-11-14 21:11:50 -080059 }
Brian O'Connorea1efbe2013-11-25 22:57:43 -080060
Brian O'Connore46492e2013-11-14 21:11:50 -080061 @Override
Brian O'Connorea1efbe2013-11-25 22:57:43 -080062 public void interrupt(IOFSwitch sw) {
63 Thread t = switchThreads.remove(sw);
Brian O'Connore46492e2013-11-14 21:11:50 -080064 if(t != null) {
65 t.interrupt();
Brian O'Connorea1efbe2013-11-25 22:57:43 -080066 }
Brian O'Connore46492e2013-11-14 21:11:50 -080067 }
68
Brian O'Connorea1efbe2013-11-25 22:57:43 -080069 public void init(IFlowPusherService pusherService) {
70 pusher = pusherService;
Brian O'Connore46492e2013-11-14 21:11:50 -080071 }
72
Naoki Shiotae3199732013-11-25 16:14:43 -080073 /**
74 * Synchronizer represents main thread of synchronization.
75 * @author Brian
76 *
77 */
78 protected class Synchronizer implements Runnable {
Brian O'Connora8e49802013-10-30 20:49:59 -070079 IOFSwitch sw;
80 ISwitchObject swObj;
Brian O'Connore46492e2013-11-14 21:11:50 -080081
Pavlin Radoslavovf8c78552013-11-26 10:56:59 -080082 public Synchronizer(IOFSwitch sw) {
Brian O'Connora8e49802013-10-30 20:49:59 -070083 this.sw = sw;
84 Dpid dpid = new Dpid(sw.getId());
85 this.swObj = dbHandler.searchSwitch(dpid.toString());
86 }
Brian O'Connore46492e2013-11-14 21:11:50 -080087
Brian O'Connora8e49802013-10-30 20:49:59 -070088 @Override
89 public void run() {
Brian O'Connorea1efbe2013-11-25 22:57:43 -080090 // TODO: stop adding other flow entries while synchronizing
91 //pusher.suspend(sw);
Brian O'Connor0d6ba512013-11-05 15:17:44 -080092 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
93 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
Brian O'Connora8e49802013-10-30 20:49:59 -070094 compare(graphEntries, switchEntries);
Brian O'Connorea1efbe2013-11-25 22:57:43 -080095 //pusher.resume(sw);
Brian O'Connora8e49802013-10-30 20:49:59 -070096 }
Brian O'Connore46492e2013-11-14 21:11:50 -080097
Naoki Shiotae3199732013-11-25 16:14:43 -080098 /**
99 * Compare flows entries in GraphDB and switch to pick up necessary messages.
100 * After picking up, picked messages are added to FlowPusher.
101 * @param graphEntries Flow entries in GraphDB.
102 * @param switchEntries Flow entries in switch.
103 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800104 private void compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800105 int added = 0, removed = 0, skipped = 0;
106 for(FlowEntryWrapper entry : switchEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800107 if(graphEntries.contains(entry)) {
108 graphEntries.remove(entry);
109 skipped++;
110 }
111 else {
112 // remove flow entry from the switch
113 entry.removeFromSwitch(sw);
114 removed++;
115 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700116 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800117 for(FlowEntryWrapper entry : graphEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800118 // add flow entry to switch
119 entry.addToSwitch(sw);
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800120 added++;
121 }
122 log.debug("Flow entries added "+ added + ", " +
123 "Flow entries removed "+ removed + ", " +
124 "Flow entries skipped " + skipped);
Brian O'Connora8e49802013-10-30 20:49:59 -0700125 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800126
Naoki Shiotae3199732013-11-25 16:14:43 -0800127 /**
128 * Read GraphDB to get FlowEntries associated with a switch.
129 * @return set of FlowEntries
130 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800131 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
132 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700133 for(IFlowEntry entry : swObj.getFlowEntries()) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800134 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
135 entries.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700136 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800137 return entries;
Brian O'Connora8e49802013-10-30 20:49:59 -0700138 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800139
Naoki Shiotae3199732013-11-25 16:14:43 -0800140 /**
141 * Read flow table from switch and derive FlowEntries from table.
142 * @return set of FlowEntries
143 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800144 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700145
146 int lengthU = 0;
147 OFMatch match = new OFMatch();
148 match.setWildcards(OFMatch.OFPFW_ALL);
149
150 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
151 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
152 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
153 stat.setMatch(match);
154 List<OFStatistics> stats = new ArrayList<OFStatistics>();
155 stats.add(stat);
156 lengthU += stat.getLength();
157
158 OFStatisticsRequest req = new OFStatisticsRequest();
159 req.setStatisticType(OFStatisticsType.FLOW);
160 req.setStatistics(stats);
161 lengthU += req.getLengthU();
162 req.setLengthU(lengthU);
163
164 List<OFStatistics> entries = null;
165 try {
166 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
167 entries = dfuture.get();
168 } catch (IOException e) {
169 // TODO Auto-generated catch block
170 e.printStackTrace();
171 } catch (InterruptedException e) {
172 // TODO Auto-generated catch block
173 e.printStackTrace();
174 } catch (ExecutionException e) {
175 // TODO Auto-generated catch block
176 e.printStackTrace();
177 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800178
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800179 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700180 for(OFStatistics result : entries){
Brian O'Connora8e49802013-10-30 20:49:59 -0700181 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800182 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
183 results.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700184 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800185 return results;
Brian O'Connora8e49802013-10-30 20:49:59 -0700186 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800187
Brian O'Connora8e49802013-10-30 20:49:59 -0700188 }
189
Naoki Shiotae3199732013-11-25 16:14:43 -0800190 /**
191 * FlowEntryWrapper represents abstract FlowEntry which is embodied by IFlowEntry
192 * (from GraphDB) or OFFlowStatisticsReply (from switch).
193 * @author Brian
194 *
195 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800196 class FlowEntryWrapper {
197 FlowEntryId id;
198 IFlowEntry iflowEntry;
199 OFFlowStatisticsReply statisticsReply;
200
201 public FlowEntryWrapper(IFlowEntry entry) {
202 iflowEntry = entry;
203 id = new FlowEntryId(entry.getFlowEntryId());
Brian O'Connora8e49802013-10-30 20:49:59 -0700204 }
205
Brian O'Connore46492e2013-11-14 21:11:50 -0800206 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
207 statisticsReply = entry;
208 id = new FlowEntryId(entry.getCookie());
209 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700210
Naoki Shiotae3199732013-11-25 16:14:43 -0800211 /**
212 * Install this FlowEntry to a switch via FlowPusher.
213 * @param sw
214 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800215 public void addToSwitch(IOFSwitch sw) {
216 if(iflowEntry != null) {
217 pusher.add(sw, iflowEntry.getFlow(), iflowEntry);
218 }
219 else if(statisticsReply != null) {
220 log.error("Adding existing flow entry {} to sw {}",
221 statisticsReply.getCookie(), sw.getId());
222 }
223 }
224
Naoki Shiotae3199732013-11-25 16:14:43 -0800225 /**
226 * Remove this FlowEntry from a switch via FlowPusher.
227 * @param sw
228 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800229 public void removeFromSwitch(IOFSwitch sw){
230 if(iflowEntry != null) {
231 log.error("Removing non-existent flow entry {} from sw {}",
232 iflowEntry.getFlowEntryId(), sw.getId());
Brian O'Connora8e49802013-10-30 20:49:59 -0700233
Brian O'Connore46492e2013-11-14 21:11:50 -0800234 }
235 else if(statisticsReply != null) {
236 // Convert Statistics Reply to Flow Mod, then write it
237 OFFlowMod fm = new OFFlowMod();
238 fm.setCookie(statisticsReply.getCookie());
239 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
240 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
241 fm.setMatch(statisticsReply.getMatch());
242 fm.setPriority(statisticsReply.getPriority());
243 fm.setOutPort(OFPort.OFPP_NONE);
244 pusher.add(sw, fm);
245 }
246 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700247
Brian O'Connore46492e2013-11-14 21:11:50 -0800248 /**
249 * Return the hash code of the Flow Entry ID
250 */
251 @Override
252 public int hashCode() {
253 return id.hashCode();
254 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700255
Brian O'Connore46492e2013-11-14 21:11:50 -0800256 /**
257 * Returns true of the object is another Flow Entry ID with
258 * the same value; otherwise, returns false.
259 *
260 * @param Object to compare
261 */
262 @Override
263 public boolean equals(Object obj){
264 if(obj.getClass() == this.getClass()) {
265 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
266 // TODO: we need to actually compare the match + actions
267 return this.id.equals(entry.id);
268 }
269 return false;
270 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800271
Brian O'Connore46492e2013-11-14 21:11:50 -0800272 @Override
273 public String toString() {
274 return id.toString();
275 }
276 }
Brian O'Connorea1efbe2013-11-25 22:57:43 -0800277
278
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800279}
280
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800281