blob: c357e7c1e9385bd6cdbad2314d0f6ae091a65d0d [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'Connora8e49802013-10-30 20:49:59 -070093 @Override
94 public void run() {
Brian O'Connorea1efbe2013-11-25 22:57:43 -080095 // TODO: stop adding other flow entries while synchronizing
96 //pusher.suspend(sw);
Brian O'Connor0d6ba512013-11-05 15:17:44 -080097 Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
98 Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
Brian O'Connora8e49802013-10-30 20:49:59 -070099 compare(graphEntries, switchEntries);
Brian O'Connorea1efbe2013-11-25 22:57:43 -0800100 //pusher.resume(sw);
Brian O'Connora8e49802013-10-30 20:49:59 -0700101 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800102
Naoki Shiotae3199732013-11-25 16:14:43 -0800103 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800104 * Compare flows entries in GraphDB and switch to pick up necessary
105 * messages.
Naoki Shiotae3199732013-11-25 16:14:43 -0800106 * After picking up, picked messages are added to FlowPusher.
107 * @param graphEntries Flow entries in GraphDB.
108 * @param switchEntries Flow entries in switch.
109 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800110 private void compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800111 int added = 0, removed = 0, skipped = 0;
112 for(FlowEntryWrapper entry : switchEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800113 if(graphEntries.contains(entry)) {
114 graphEntries.remove(entry);
115 skipped++;
116 }
117 else {
118 // remove flow entry from the switch
119 entry.removeFromSwitch(sw);
120 removed++;
121 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700122 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800123 for(FlowEntryWrapper entry : graphEntries) {
Brian O'Connore46492e2013-11-14 21:11:50 -0800124 // add flow entry to switch
125 entry.addToSwitch(sw);
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800126 added++;
127 }
128 log.debug("Flow entries added "+ added + ", " +
129 "Flow entries removed "+ removed + ", " +
130 "Flow entries skipped " + skipped);
Brian O'Connora8e49802013-10-30 20:49:59 -0700131 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800132
Naoki Shiotae3199732013-11-25 16:14:43 -0800133 /**
134 * Read GraphDB to get FlowEntries associated with a switch.
135 * @return set of FlowEntries
136 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800137 private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
138 Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700139 for(IFlowEntry entry : swObj.getFlowEntries()) {
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800140 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
141 entries.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700142 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800143 return entries;
Brian O'Connora8e49802013-10-30 20:49:59 -0700144 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800145
Naoki Shiotae3199732013-11-25 16:14:43 -0800146 /**
147 * Read flow table from switch and derive FlowEntries from table.
148 * @return set of FlowEntries
149 */
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800150 private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
Brian O'Connora8e49802013-10-30 20:49:59 -0700151
152 int lengthU = 0;
153 OFMatch match = new OFMatch();
154 match.setWildcards(OFMatch.OFPFW_ALL);
155
156 OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
157 stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
158 stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
159 stat.setMatch(match);
160 List<OFStatistics> stats = new ArrayList<OFStatistics>();
161 stats.add(stat);
162 lengthU += stat.getLength();
163
164 OFStatisticsRequest req = new OFStatisticsRequest();
165 req.setStatisticType(OFStatisticsType.FLOW);
166 req.setStatistics(stats);
167 lengthU += req.getLengthU();
168 req.setLengthU(lengthU);
169
170 List<OFStatistics> entries = null;
171 try {
172 Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
173 entries = dfuture.get();
174 } catch (IOException e) {
175 // TODO Auto-generated catch block
176 e.printStackTrace();
177 } catch (InterruptedException e) {
178 // TODO Auto-generated catch block
179 e.printStackTrace();
180 } catch (ExecutionException e) {
181 // TODO Auto-generated catch block
182 e.printStackTrace();
183 }
Brian O'Connore46492e2013-11-14 21:11:50 -0800184
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800185 Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
Brian O'Connora8e49802013-10-30 20:49:59 -0700186 for(OFStatistics result : entries){
Brian O'Connora8e49802013-10-30 20:49:59 -0700187 OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800188 FlowEntryWrapper fe = new FlowEntryWrapper(entry);
189 results.add(fe);
Brian O'Connora8e49802013-10-30 20:49:59 -0700190 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800191 return results;
Brian O'Connora8e49802013-10-30 20:49:59 -0700192 }
Brian O'Connor8c166a72013-11-14 18:41:48 -0800193
Brian O'Connora8e49802013-10-30 20:49:59 -0700194 }
195
Naoki Shiotae3199732013-11-25 16:14:43 -0800196 /**
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800197 * FlowEntryWrapper represents abstract FlowEntry which is embodied
198 * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
Naoki Shiotae3199732013-11-25 16:14:43 -0800199 * @author Brian
200 *
201 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800202 class FlowEntryWrapper {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800203 FlowEntryId flowEntryId;
Brian O'Connore46492e2013-11-14 21:11:50 -0800204 OFFlowStatisticsReply statisticsReply;
205
206 public FlowEntryWrapper(IFlowEntry entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800207 flowEntryId = new FlowEntryId(entry.getFlowEntryId());
Brian O'Connora8e49802013-10-30 20:49:59 -0700208 }
209
Brian O'Connore46492e2013-11-14 21:11:50 -0800210 public FlowEntryWrapper(OFFlowStatisticsReply entry) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800211 flowEntryId = new FlowEntryId(entry.getCookie());
Brian O'Connore46492e2013-11-14 21:11:50 -0800212 statisticsReply = entry;
Brian O'Connore46492e2013-11-14 21:11:50 -0800213 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700214
Naoki Shiotae3199732013-11-25 16:14:43 -0800215 /**
216 * Install this FlowEntry to a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800217 * @param sw Switch to which flow will be installed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800218 */
Brian O'Connore46492e2013-11-14 21:11:50 -0800219 public void addToSwitch(IOFSwitch sw) {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800220 if (statisticsReply != null) {
221 log.error("Error adding existing flow entry {} to sw {}",
Brian O'Connore46492e2013-11-14 21:11:50 -0800222 statisticsReply.getCookie(), sw.getId());
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800223 return;
Brian O'Connore46492e2013-11-14 21:11:50 -0800224 }
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800225
226 // Get the Flow Entry state from the Network Graph
227 IFlowEntry iFlowEntry = null;
228 try {
229 iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
230 } catch (Exception e) {
231 log.error("Error finding flow entry {} in Network Graph",
232 flowEntryId);
233 return;
234 }
235 if (iFlowEntry == null) {
236 log.error("Cannot add flow entry {} to sw {} : flow entry not found",
237 flowEntryId, sw.getId());
238 return;
239 }
240
Pavlin Radoslavov6bfaea62013-12-03 14:55:57 -0800241 FlowEntry flowEntry =
242 FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
243 if (flowEntry == null) {
244 log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
245 flowEntryId, sw.getId());
246 return;
247 }
248
249 pusher.add(sw, flowEntry);
Brian O'Connore46492e2013-11-14 21:11:50 -0800250 }
251
Naoki Shiotae3199732013-11-25 16:14:43 -0800252 /**
253 * Remove this FlowEntry from a switch via FlowPusher.
Naoki Shiotab485d412013-11-26 12:04:19 -0800254 * @param sw Switch from which flow will be removed.
Naoki Shiotae3199732013-11-25 16:14:43 -0800255 */
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800256 public void removeFromSwitch(IOFSwitch sw) {
257 if (statisticsReply == null) {
258 log.error("Error removing non-existent flow entry {} from sw {}",
259 flowEntryId, sw.getId());
260 return;
261 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700262
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800263 // Convert Statistics Reply to Flow Mod, then write it
264 OFFlowMod fm = new OFFlowMod();
265 fm.setCookie(statisticsReply.getCookie());
266 fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
267 fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
268 fm.setMatch(statisticsReply.getMatch());
269 fm.setPriority(statisticsReply.getPriority());
270 fm.setOutPort(OFPort.OFPP_NONE);
271
272 pusher.add(sw, fm);
Brian O'Connore46492e2013-11-14 21:11:50 -0800273 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700274
Brian O'Connore46492e2013-11-14 21:11:50 -0800275 /**
276 * Return the hash code of the Flow Entry ID
277 */
278 @Override
279 public int hashCode() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800280 return flowEntryId.hashCode();
Brian O'Connore46492e2013-11-14 21:11:50 -0800281 }
Brian O'Connora8e49802013-10-30 20:49:59 -0700282
Brian O'Connore46492e2013-11-14 21:11:50 -0800283 /**
284 * Returns true of the object is another Flow Entry ID with
285 * the same value; otherwise, returns false.
286 *
287 * @param Object to compare
Naoki Shiotab485d412013-11-26 12:04:19 -0800288 * @return true if the object has the same Flow Entry ID.
Brian O'Connore46492e2013-11-14 21:11:50 -0800289 */
290 @Override
291 public boolean equals(Object obj){
292 if(obj.getClass() == this.getClass()) {
293 FlowEntryWrapper entry = (FlowEntryWrapper) obj;
294 // TODO: we need to actually compare the match + actions
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800295 return this.flowEntryId.equals(entry.flowEntryId);
Brian O'Connore46492e2013-11-14 21:11:50 -0800296 }
297 return false;
298 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800299
Brian O'Connore46492e2013-11-14 21:11:50 -0800300 @Override
301 public String toString() {
Pavlin Radoslavov07fb9972013-12-02 16:20:24 -0800302 return flowEntryId.toString();
Brian O'Connore46492e2013-11-14 21:11:50 -0800303 }
304 }
Brian O'Connor0d6ba512013-11-05 15:17:44 -0800305}