blob: 0e37e17d1667df64c03df2889e2e64908d98f5cd [file] [log] [blame]
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001package net.floodlightcontroller.flowcache;
2
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08003import java.io.IOException;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08004import java.util.ArrayList;
5import java.util.Collection;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08006import java.util.EnumSet;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08007import java.util.HashMap;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07008import java.util.HashSet;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +00009import java.util.LinkedList;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080010import java.util.List;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080011import java.util.Map;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070012import java.util.TreeMap;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070013import java.util.Collections;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070014import java.util.concurrent.BlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080015import java.util.concurrent.Executors;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070016import java.util.concurrent.LinkedBlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080017import java.util.concurrent.ScheduledExecutorService;
18import java.util.concurrent.ScheduledFuture;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070019import java.util.concurrent.ThreadPoolExecutor;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080020import java.util.concurrent.TimeUnit;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080021
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080022import net.floodlightcontroller.core.IFloodlightProviderService;
23import net.floodlightcontroller.core.INetMapStorage;
24import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
25import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070026import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
Pankaj Berded0079742013-03-27 17:53:25 -070027import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070028import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080029import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080030import net.floodlightcontroller.core.module.FloodlightModuleContext;
31import net.floodlightcontroller.core.module.FloodlightModuleException;
32import net.floodlightcontroller.core.module.IFloodlightModule;
33import net.floodlightcontroller.core.module.IFloodlightService;
34import net.floodlightcontroller.flowcache.IFlowService;
35import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
36import net.floodlightcontroller.restserver.IRestApiService;
37import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080038import net.floodlightcontroller.util.DataPath;
39import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080040import net.floodlightcontroller.util.DataPathEndpoints;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080041import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070042import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080043import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070044import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080045import net.floodlightcontroller.util.FlowEntrySwitchState;
46import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080047import net.floodlightcontroller.util.FlowId;
48import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070049import net.floodlightcontroller.util.IPv4Net;
50import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080051import net.floodlightcontroller.util.OFMessageDamper;
52import net.floodlightcontroller.util.Port;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070053import net.floodlightcontroller.util.SwitchPort;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070054import net.onrc.onos.flow.IFlowManager;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080055import net.onrc.onos.util.GraphDBConnection;
56import net.onrc.onos.util.GraphDBConnection.Transaction;
57
58import org.openflow.protocol.OFFlowMod;
59import org.openflow.protocol.OFMatch;
60import org.openflow.protocol.OFPacketOut;
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070061import org.openflow.protocol.OFPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080062import org.openflow.protocol.OFType;
63import org.openflow.protocol.action.OFAction;
64import org.openflow.protocol.action.OFActionOutput;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080065
66import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
68
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070069public class FlowManager implements IFloodlightModule, IFlowService, IFlowManager, INetMapStorage {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080070
71 public GraphDBConnection conn;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080072
73 protected IRestApiService restApi;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080074 protected IFloodlightProviderService floodlightProvider;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070075 protected ITopoRouteService topoRouteService;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070076 protected FloodlightModuleContext context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080077
78 protected OFMessageDamper messageDamper;
79
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070080 //
81 // TODO: Values copied from elsewhere (class LearningSwitch).
82 // The local copy should go away!
83 //
84 protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
85 protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
86 public static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0; // infinity
87 public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
88 public static final short PRIORITY_DEFAULT = 100;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080089
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070090 private static long nextFlowEntryId = 1;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070091 private static long measurementFlowId = 100000;
92 private static String measurementFlowIdStr = "0x186a0"; // 100000
93 private long modifiedMeasurementFlowTime = 0;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070094
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080095 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080096 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
97
98 // The periodic task(s)
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070099 private final ScheduledExecutorService measureShortestPathScheduler =
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800100 Executors.newScheduledThreadPool(1);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700101 private final ScheduledExecutorService measureMapReaderScheduler =
102 Executors.newScheduledThreadPool(1);
103 private final ScheduledExecutorService mapReaderScheduler =
104 Executors.newScheduledThreadPool(1);
105
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700106 private BlockingQueue<Runnable> shortestPathQueue = new LinkedBlockingQueue<Runnable>();
107 private ThreadPoolExecutor shortestPathExecutor =
108 new ThreadPoolExecutor(10, 10, 5, TimeUnit.SECONDS, shortestPathQueue);
109
110 class ShortestPathTask implements Runnable {
111 private int hint;
112 private ITopoRouteService topoRouteService;
113 private ArrayList<DataPath> dpList;
114
115 public ShortestPathTask(int hint,
116 ITopoRouteService topoRouteService,
117 ArrayList<DataPath> dpList) {
118 this.hint = hint;
119 this.topoRouteService = topoRouteService;
120 this.dpList = dpList;
121 }
122
123 @Override
124 public void run() {
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700125 /*
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700126 String logMsg = "MEASUREMENT: Running Thread hint " + this.hint;
127 log.debug(logMsg);
128 long startTime = System.nanoTime();
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700129 */
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700130 for (DataPath dp : this.dpList) {
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700131 topoRouteService.getTopoShortestPath(dp.srcPort(), dp.dstPort());
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700132 }
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700133 /*
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700134 long estimatedTime = System.nanoTime() - startTime;
135 double rate = (estimatedTime > 0)? ((double)dpList.size() * 1000000000) / estimatedTime: 0.0;
136 logMsg = "MEASUREMENT: Computed Thread hint " + hint + ": " + dpList.size() + " shortest paths in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " flows/s";
137 log.debug(logMsg);
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700138 */
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700139 }
140 }
141
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700142 final Runnable measureShortestPath = new Runnable() {
143 public void run() {
144 log.debug("Recomputing Shortest Paths from the Network Map Flows...");
145 if (floodlightProvider == null) {
146 log.debug("FloodlightProvider service not found!");
147 return;
148 }
149
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700150 if (topoRouteService == null) {
151 log.debug("Topology Route Service not found");
152 return;
153 }
154
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700155 int leftoverQueueSize = shortestPathExecutor.getQueue().size();
156 if (leftoverQueueSize > 0) {
157 String logMsg = "MEASUREMENT: Leftover Shortest Path Queue Size: " + leftoverQueueSize;
158 log.debug(logMsg);
159 return;
160 }
161 log.debug("MEASUREMENT: Beginning Shortest Path Computation");
162
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700163 //
164 // Recompute the Shortest Paths for all Flows
165 //
166 int counter = 0;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700167 int hint = 0;
168 ArrayList<DataPath> dpList = new ArrayList<DataPath>();
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700169 long startTime = System.nanoTime();
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700170
171 topoRouteService.prepareShortestPathTopo();
172
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700173 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
174 for (IFlowPath flowPathObj : allFlowPaths) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700175 FlowId flowId = new FlowId(flowPathObj.getFlowId());
176
177 // log.debug("Found Path {}", flowId.toString());
178 Dpid srcDpid = new Dpid(flowPathObj.getSrcSwitch());
179 Port srcPort = new Port(flowPathObj.getSrcPort());
180 Dpid dstDpid = new Dpid(flowPathObj.getDstSwitch());
181 Port dstPort = new Port(flowPathObj.getDstPort());
182 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
183 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700184
185 /*
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700186 DataPath dp = new DataPath();
187 dp.setSrcPort(srcSwitchPort);
188 dp.setDstPort(dstSwitchPort);
189 dpList.add(dp);
190 if ((dpList.size() % 10) == 0) {
191 shortestPathExecutor.execute(
192 new ShortestPathTask(hint, topoRouteService,
193 dpList));
194 dpList = new ArrayList<DataPath>();
195 hint++;
196 }
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700197 */
198
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700199 DataPath dataPath =
200 topoRouteService.getTopoShortestPath(srcSwitchPort,
201 dstSwitchPort);
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700202 counter++;
203 }
204 if (dpList.size() > 0) {
205 shortestPathExecutor.execute(
206 new ShortestPathTask(hint, topoRouteService,
207 dpList));
208 }
209
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700210 /*
Pavlin Radoslavov4da61282013-03-20 20:31:36 -0700211 // Wait for all tasks to finish
212 try {
213 while (shortestPathExecutor.getQueue().size() > 0) {
214 Thread.sleep(100);
215 }
216 } catch (InterruptedException ex) {
217 log.debug("MEASUREMENT: Shortest Path Computation interrupted");
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700218 }
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700219 */
220
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700221 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavova5f167b2013-03-21 11:39:27 -0700222 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700223
224 long estimatedTime = System.nanoTime() - startTime;
225 double rate = (estimatedTime > 0)? ((double)counter * 1000000000) / estimatedTime: 0.0;
226 String logMsg = "MEASUREMENT: Computed " + counter + " shortest paths in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " flows/s";
227 log.debug(logMsg);
228 }
229 };
230
231 final Runnable measureMapReader = new Runnable() {
232 public void run() {
233 if (floodlightProvider == null) {
234 log.debug("FloodlightProvider service not found!");
235 return;
236 }
237
238 //
239 // Fetch all Flow Entries
240 //
241 int counter = 0;
242 long startTime = System.nanoTime();
243 Iterable<IFlowEntry> allFlowEntries = conn.utils().getAllFlowEntries(conn);
244 for (IFlowEntry flowEntryObj : allFlowEntries) {
245 counter++;
246 FlowEntryId flowEntryId =
247 new FlowEntryId(flowEntryObj.getFlowEntryId());
248 String userState = flowEntryObj.getUserState();
249 String switchState = flowEntryObj.getSwitchState();
250 }
251 conn.endTx(Transaction.COMMIT);
252
253 long estimatedTime = System.nanoTime() - startTime;
254 double rate = (estimatedTime > 0)? ((double)counter * 1000000000) / estimatedTime: 0.0;
255 String logMsg = "MEASUREMENT: Fetched " + counter + " flow entries in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " entries/s";
256 log.debug(logMsg);
257 }
258 };
259
260 final Runnable mapReader = new Runnable() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800261 public void run() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800262 if (floodlightProvider == null) {
263 log.debug("FloodlightProvider service not found!");
264 return;
265 }
266
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000267 Map<Long, IOFSwitch> mySwitches =
268 floodlightProvider.getSwitches();
269 Map<Long, IFlowEntry> myFlowEntries =
270 new TreeMap<Long, IFlowEntry>();
271 LinkedList<IFlowEntry> deleteFlowEntries =
272 new LinkedList<IFlowEntry>();
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700273
274 //
275 // Fetch all Flow Entries and select only my Flow Entries
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000276 // that need to be undated into the switches.
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700277 //
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000278 Iterable<IFlowEntry> allFlowEntries =
279 conn.utils().getAllFlowEntries(conn);
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700280 for (IFlowEntry flowEntryObj : allFlowEntries) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000281 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700282 String userState = flowEntryObj.getUserState();
283 String switchState = flowEntryObj.getSwitchState();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000284 String dpidStr = flowEntryObj.getSwitchDpid();
285 if ((flowEntryIdStr == null) ||
286 (userState == null) ||
287 (switchState == null) ||
288 (dpidStr == null)) {
289 log.debug("IGNORING Flow Entry entry with null fields");
290 continue;
291 }
292 FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
293 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800294
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000295 /*
296 log.debug("Found Flow Entry Id = {} {}",
Pavlin Radoslavov79a67c12013-03-15 21:05:53 -0700297 flowEntryId.toString(),
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000298 "DPID = " + dpid.toString() +
299 " User State: " + userState +
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700300 " Switch State: " + switchState);
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000301 */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800302
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000303 if (! switchState.equals("FE_SWITCH_NOT_UPDATED"))
304 continue; // Ignore the entry: nothing to do
305
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800306 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000307 if (mySwitch == null)
308 continue; // Ignore the entry: not my switch
309
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700310 myFlowEntries.put(flowEntryId.value(), flowEntryObj);
311 }
312
313 //
314 // Process my Flow Entries
315 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700316 boolean processed_measurement_flow = false;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700317 for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
318 IFlowEntry flowEntryObj = entry.getValue();
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700319 // Code for measurement purpose
320 {
321 IFlowPath flowObj =
322 conn.utils().getFlowPathByFlowEntry(conn,
323 flowEntryObj);
324 if ((flowObj != null) &&
325 flowObj.getFlowId().equals(measurementFlowIdStr)) {
326 processed_measurement_flow = true;
327 }
328 }
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700329
330 //
331 // TODO: Eliminate the re-fetching of flowEntryId,
332 // userState, switchState, and dpid from the flowEntryObj.
333 //
334 FlowEntryId flowEntryId =
335 new FlowEntryId(flowEntryObj.getFlowEntryId());
336 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
337 String userState = flowEntryObj.getUserState();
338 String switchState = flowEntryObj.getSwitchState();
339 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000340 if (mySwitch == null)
341 continue; // Shouldn't happen
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800342
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700343 // TODO: PAVPAVPAV: FROM HERE...
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800344 //
345 // Create the Open Flow Flow Modification Entry to push
346 //
347 OFFlowMod fm =
348 (OFFlowMod) floodlightProvider.getOFMessageFactory()
349 .getMessage(OFType.FLOW_MOD);
350 long cookie = flowEntryId.value();
351
352 short flowModCommand = OFFlowMod.OFPFC_ADD;
353 if (userState.equals("FE_USER_ADD")) {
354 flowModCommand = OFFlowMod.OFPFC_ADD;
355 } else if (userState.equals("FE_USER_MODIFY")) {
356 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
357 } else if (userState.equals("FE_USER_DELETE")) {
358 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
359 } else {
360 // Unknown user state. Ignore the entry
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700361 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
362 flowEntryId.toString(), userState);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800363 continue;
364 }
365
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700366 //
367 // Fetch the match conditions
368 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800369 OFMatch match = new OFMatch();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700370 match.setWildcards(OFMatch.OFPFW_ALL);
371 Short matchInPort = flowEntryObj.getMatchInPort();
372 if (matchInPort != null) {
373 match.setInputPort(matchInPort);
374 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
375 }
376 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
377 if (matchEthernetFrameType != null) {
378 match.setDataLayerType(matchEthernetFrameType);
379 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
380 }
381 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
382 if (matchSrcIPv4Net != null) {
383 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
384 }
385 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
386 if (matchDstIPv4Net != null) {
387 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
388 }
389 String matchSrcMac = flowEntryObj.getMatchSrcMac();
390 if (matchSrcMac != null) {
391 match.setDataLayerSource(matchSrcMac);
392 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
393 }
394 String matchDstMac = flowEntryObj.getMatchDstMac();
395 if (matchDstMac != null) {
396 match.setDataLayerDestination(matchDstMac);
397 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
398 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700399
400 //
401 // Fetch the actions
402 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800403 List<OFAction> actions = new ArrayList<OFAction>();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700404 Short actionOutputPort = flowEntryObj.getActionOutput();
405 if (actionOutputPort != null) {
406 OFActionOutput action = new OFActionOutput();
407 // XXX: The max length is hard-coded for now
408 action.setMaxLength((short)0xffff);
409 action.setPort(actionOutputPort);
410 actions.add(action);
411 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800412
413 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
414 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700415 .setPriority(PRIORITY_DEFAULT)
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800416 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
417 .setCookie(cookie)
418 .setCommand(flowModCommand)
419 .setMatch(match)
420 .setActions(actions)
421 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700422 fm.setOutPort(OFPort.OFPP_NONE.getValue());
423 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
424 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
425 if (actionOutputPort != null)
426 fm.setOutPort(actionOutputPort);
427 }
428
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800429 //
430 // TODO: Set the following flag
431 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
432 // See method ForwardingBase::pushRoute()
433 //
434 try {
435 messageDamper.write(mySwitch, fm, null);
436 mySwitch.flush();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000437 //
438 // TODO: We should use the OpenFlow Barrier mechanism
439 // to check for errors, and update the SwitchState
440 // for a flow entry after the Barrier message is
441 // is received.
442 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800443 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
444 if (userState.equals("FE_USER_DELETE")) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000445 // An entry that needs to be deleted.
446 deleteFlowEntries.add(flowEntryObj);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800447 }
448 } catch (IOException e) {
449 log.error("Failure writing flow mod from network map", e);
450 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700451 // TODO: XXX: PAVPAVPAV: TO HERE.
452 // TODO: XXX: PAVPAVPAV: Update the flowEntryObj
453 // to "FE_SWITCH_UPDATED" in the new (refactored) code.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800454 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000455
456 //
457 // Delete all entries marked for deletion
458 //
459 // TODO: We should use the OpenFlow Barrier mechanism
460 // to check for errors, and delete the Flow Entries after the
461 // Barrier message is received.
462 //
463 while (! deleteFlowEntries.isEmpty()) {
464 IFlowEntry flowEntryObj = deleteFlowEntries.poll();
465 IFlowPath flowObj =
466 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
467 if (flowObj == null) {
468 log.debug("Did not find FlowPath to be deleted");
469 continue;
470 }
471 flowObj.removeFlowEntry(flowEntryObj);
472 conn.utils().removeFlowEntry(conn, flowEntryObj);
473
474 // Test whether the last flow entry
475 Iterable<IFlowEntry> tmpflowEntries =
476 flowObj.getFlowEntries();
477 boolean found = false;
478 for (IFlowEntry tmpflowEntryObj : tmpflowEntries) {
479 found = true;
480 break;
481 }
482 if (! found) {
483 // Remove the Flow Path as well
484 conn.utils().removeFlowPath(conn, flowObj);
485 }
486 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800487 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700488
489 if (processed_measurement_flow) {
490 long estimatedTime = System.nanoTime() - modifiedMeasurementFlowTime;
491 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
492 (double)estimatedTime / 1000000000 + " sec";
493 log.debug(logMsg);
494 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800495 }
496 };
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700497
498 /*
499 final ScheduledFuture<?> measureShortestPathHandle =
500 measureShortestPathScheduler.scheduleAtFixedRate(measureShortestPath, 10, 10, TimeUnit.SECONDS);
501 */
502
Pavlin Radoslavove38319c2013-03-21 16:20:00 -0700503 /*
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700504 final ScheduledFuture<?> measureMapReaderHandle =
505 measureMapReaderScheduler.scheduleAtFixedRate(measureMapReader, 10, 10, TimeUnit.SECONDS);
Pavlin Radoslavove38319c2013-03-21 16:20:00 -0700506 */
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700507
508 final ScheduledFuture<?> mapReaderHandle =
509 mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800510
511 @Override
512 public void init(String conf) {
513 conn = GraphDBConnection.getInstance(conf);
514 }
515
516 public void finalize() {
517 close();
518 }
519
520 @Override
521 public void close() {
522 conn.close();
523 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800524
525 @Override
526 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
527 Collection<Class<? extends IFloodlightService>> l =
528 new ArrayList<Class<? extends IFloodlightService>>();
529 l.add(IFlowService.class);
530 return l;
531 }
532
533 @Override
534 public Map<Class<? extends IFloodlightService>, IFloodlightService>
535 getServiceImpls() {
536 Map<Class<? extends IFloodlightService>,
537 IFloodlightService> m =
538 new HashMap<Class<? extends IFloodlightService>,
539 IFloodlightService>();
540 m.put(IFlowService.class, this);
541 return m;
542 }
543
544 @Override
545 public Collection<Class<? extends IFloodlightService>>
546 getModuleDependencies() {
547 Collection<Class<? extends IFloodlightService>> l =
548 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800549 l.add(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700550 l.add(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800551 l.add(IRestApiService.class);
552 return l;
553 }
554
555 @Override
556 public void init(FloodlightModuleContext context)
557 throws FloodlightModuleException {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700558 this.context = context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800559 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700560 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800561 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800562 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
563 EnumSet.of(OFType.FLOW_MOD),
564 OFMESSAGE_DAMPER_TIMEOUT);
565 // TODO: An ugly hack!
566 String conf = "/tmp/cassandra.titan";
567 this.init(conf);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800568 }
569
570 @Override
571 public void startUp(FloodlightModuleContext context) {
572 restApi.addRestletRoutable(new FlowWebRoutable());
Pavlin Radoslavov80ca6302013-03-20 02:08:09 -0700573
574 //
575 // Extract all flow entries and assign the next Flow Entry ID
576 // to be larger than the largest Flow Entry ID
577 //
578 Iterable<IFlowEntry> allFlowEntries = conn.utils().getAllFlowEntries(conn);
579 for (IFlowEntry flowEntryObj : allFlowEntries) {
580 FlowEntryId flowEntryId =
581 new FlowEntryId(flowEntryObj.getFlowEntryId());
582 if (flowEntryId.value() >= nextFlowEntryId)
583 nextFlowEntryId = flowEntryId.value() + 1;
584 }
585 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800586 }
587
588 /**
589 * Add a flow.
590 *
591 * Internally, ONOS will automatically register the installer for
592 * receiving Flow Path Notifications for that path.
593 *
594 * @param flowPath the Flow Path to install.
595 * @param flowId the return-by-reference Flow ID as assigned internally.
596 * @return true on success, otherwise false.
597 */
598 @Override
599 public boolean addFlow(FlowPath flowPath, FlowId flowId) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700600 if (flowPath.flowId().value() == measurementFlowId) {
601 modifiedMeasurementFlowTime = System.nanoTime();
602 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800603
604 //
605 // Assign the FlowEntry IDs
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700606 // Right now every new flow entry gets a new flow entry ID
607 // TODO: This needs to be redesigned!
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800608 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800609 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700610 long id = nextFlowEntryId++;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800611 flowEntry.setFlowEntryId(new FlowEntryId(id));
612 }
613
614 IFlowPath flowObj = null;
615 try {
616 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
617 != null) {
618 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
619 flowPath.flowId().toString());
620 } else {
621 flowObj = conn.utils().newFlowPath(conn);
622 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
623 flowPath.flowId().toString());
624 }
625 } catch (Exception e) {
626 // TODO: handle exceptions
627 conn.endTx(Transaction.ROLLBACK);
628 log.error(":addFlow FlowId:{} failed",
629 flowPath.flowId().toString());
630 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700631 if (flowObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000632 log.error(":addFlow FlowId:{} failed: Flow object not created",
633 flowPath.flowId().toString());
634 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800635 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700636 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800637
638 //
639 // Set the Flow key:
640 // - flowId
641 //
642 flowObj.setFlowId(flowPath.flowId().toString());
643 flowObj.setType("flow");
644
645 //
646 // Set the Flow attributes:
647 // - flowPath.installerId()
648 // - flowPath.dataPath().srcPort()
649 // - flowPath.dataPath().dstPort()
650 //
651 flowObj.setInstallerId(flowPath.installerId().toString());
652 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
653 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
654 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
655 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
656
657 // Flow edges:
658 // HeadFE
659
660
661 //
662 // Flow Entries:
663 // flowPath.dataPath().flowEntries()
664 //
665 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
666 IFlowEntry flowEntryObj = null;
667 boolean found = false;
668 try {
669 if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
670 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
671 flowEntry.flowEntryId().toString());
672 found = true;
673 } else {
674 flowEntryObj = conn.utils().newFlowEntry(conn);
675 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
676 flowEntry.flowEntryId().toString());
677 }
678 } catch (Exception e) {
679 // TODO: handle exceptions
680 conn.endTx(Transaction.ROLLBACK);
681 log.error(":addFlow FlowEntryId:{} failed",
682 flowEntry.flowEntryId().toString());
683 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700684 if (flowEntryObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000685 log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created",
686 flowEntry.flowEntryId().toString());
687 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800688 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700689 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800690
691 //
692 // Set the Flow Entry key:
693 // - flowEntry.flowEntryId()
694 //
695 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
696 flowEntryObj.setType("flow_entry");
697
698 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700699 // Set the Flow Entry Edges and attributes:
700 // - Switch edge
701 // - InPort edge
702 // - OutPort edge
703 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800704 // - flowEntry.flowEntryMatch()
705 // - flowEntry.flowEntryActions()
706 // - flowEntry.dpid()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800707 // - flowEntry.flowEntryUserState()
708 // - flowEntry.flowEntrySwitchState()
709 // - flowEntry.flowEntryErrorState()
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700710 // - flowEntry.matchInPort()
711 // - flowEntry.matchEthernetFrameType()
712 // - flowEntry.matchSrcIPv4Net()
713 // - flowEntry.matchDstIPv4Net()
714 // - flowEntry.matchSrcMac()
715 // - flowEntry.matchDstMac()
716 // - flowEntry.actionOutput()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800717 //
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700718 ISwitchObject sw =
719 conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800720 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
Pankaj Berded0079742013-03-27 17:53:25 -0700721 flowEntryObj.setSwitch(sw);
722 if (flowEntry.flowEntryMatch().matchInPort()) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700723 IPortObject inport =
724 conn.utils().searchPort(conn, flowEntry.dpid().toString(),
725 flowEntry.flowEntryMatch().inPort().value());
Pankaj Berded0079742013-03-27 17:53:25 -0700726 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
727 flowEntryObj.setInPort(inport);
728 }
729 if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
730 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
731 }
732 if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
733 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
734 }
735 if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
736 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
737 }
738 if (flowEntry.flowEntryMatch().matchSrcMac()) {
739 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
740 }
741 if (flowEntry.flowEntryMatch().matchDstMac()) {
742 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
743 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700744
745 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
Pankaj Berded0079742013-03-27 17:53:25 -0700746 if (fa.actionOutput() != null) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700747 IPortObject outport =
748 conn.utils().searchPort(conn,
749 flowEntry.dpid().toString(),
750 fa.actionOutput().port().value());
751 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
752 flowEntryObj.setOutPort(outport);
Pankaj Berded0079742013-03-27 17:53:25 -0700753 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700754 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800755 // TODO: Hacks with hard-coded state names!
756 if (found)
757 flowEntryObj.setUserState("FE_USER_MODIFY");
758 else
759 flowEntryObj.setUserState("FE_USER_ADD");
760 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
761 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700762 // TODO: Take care of the FlowEntryErrorState.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800763 //
764
765 // Flow Entries edges:
766 // Flow
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700767 // NextFE (TODO)
768 if (! found) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800769 flowObj.addFlowEntry(flowEntryObj);
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700770 flowEntryObj.setFlow(flowObj);
771 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800772 }
773 conn.endTx(Transaction.COMMIT);
774
775 //
776 // TODO: We need a proper Flow ID allocation mechanism.
777 //
778 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700779
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800780 return true;
781 }
782
783 /**
784 * Delete a previously added flow.
785 *
786 * @param flowId the Flow ID of the flow to delete.
787 * @return true on success, otherwise false.
788 */
789 @Override
790 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700791 if (flowId.value() == measurementFlowId) {
792 modifiedMeasurementFlowTime = System.nanoTime();
793 }
794
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800795 IFlowPath flowObj = null;
796 //
797 // We just mark the entries for deletion,
798 // and let the switches remove each individual entry after
799 // it has been removed from the switches.
800 //
801 try {
802 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
803 != null) {
804 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
805 flowId.toString());
806 } else {
807 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
808 flowId.toString());
809 }
810 } catch (Exception e) {
811 // TODO: handle exceptions
812 conn.endTx(Transaction.ROLLBACK);
813 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
814 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700815 if (flowObj == null) {
816 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800817 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700818 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800819
820 //
821 // Find and mark for deletion all Flow Entries
822 //
823 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
824 boolean empty = true; // TODO: an ugly hack
825 for (IFlowEntry flowEntryObj : flowEntries) {
826 empty = false;
827 // flowObj.removeFlowEntry(flowEntryObj);
828 // conn.utils().removeFlowEntry(conn, flowEntryObj);
829 flowEntryObj.setUserState("FE_USER_DELETE");
830 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
831 }
832 // Remove from the database empty flows
833 if (empty)
834 conn.utils().removeFlowPath(conn, flowObj);
835 conn.endTx(Transaction.COMMIT);
836
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800837 return true;
838 }
839
840 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700841 * Clear the state for a previously added flow.
842 *
843 * @param flowId the Flow ID of the flow to clear.
844 * @return true on success, otherwise false.
845 */
846 @Override
847 public boolean clearFlow(FlowId flowId) {
848 IFlowPath flowObj = null;
849 try {
850 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
851 != null) {
852 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
853 flowId.toString());
854 } else {
855 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
856 flowId.toString());
857 }
858 } catch (Exception e) {
859 // TODO: handle exceptions
860 conn.endTx(Transaction.ROLLBACK);
861 log.error(":clearFlow FlowId:{} failed", flowId.toString());
862 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700863 if (flowObj == null) {
864 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700865 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700866 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700867
868 //
869 // Remove all Flow Entries
870 //
871 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
872 for (IFlowEntry flowEntryObj : flowEntries) {
873 flowObj.removeFlowEntry(flowEntryObj);
874 conn.utils().removeFlowEntry(conn, flowEntryObj);
875 }
876 // Remove the Flow itself
877 conn.utils().removeFlowPath(conn, flowObj);
878 conn.endTx(Transaction.COMMIT);
879
880 return true;
881 }
882
883 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800884 * Get a previously added flow.
885 *
886 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800887 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800888 */
889 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800890 public FlowPath getFlow(FlowId flowId) {
891 IFlowPath flowObj = null;
892 try {
893 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
894 != null) {
895 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
896 flowId.toString());
897 } else {
898 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
899 flowId.toString());
900 }
901 } catch (Exception e) {
902 // TODO: handle exceptions
903 conn.endTx(Transaction.ROLLBACK);
904 log.error(":getFlow FlowId:{} failed", flowId.toString());
905 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700906 if (flowObj == null) {
907 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800908 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700909 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800910
911 //
912 // Extract the Flow state
913 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800914 FlowPath flowPath = extractFlowPath(flowObj);
915 conn.endTx(Transaction.COMMIT);
916
917 return flowPath;
918 }
919
920 /**
921 * Get all previously added flows by a specific installer for a given
922 * data path endpoints.
923 *
924 * @param installerId the Caller ID of the installer of the flow to get.
925 * @param dataPathEndpoints the data path endpoints of the flow to get.
926 * @return the Flow Paths if found, otherwise null.
927 */
928 @Override
929 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
930 DataPathEndpoints dataPathEndpoints) {
931 //
932 // TODO: The implementation below is not optimal:
933 // We fetch all flows, and then return only the subset that match
934 // the query conditions.
935 // We should use the appropriate Titan/Gremlin query to filter-out
936 // the flows as appropriate.
937 //
938 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700939 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800940
941 if (allFlows == null) {
942 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700943 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800944 }
945
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800946 for (FlowPath flow : allFlows) {
947 //
948 // TODO: String-based comparison is sub-optimal.
949 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800950 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800951 //
952 if (! flow.installerId().toString().equals(installerId.toString()))
953 continue;
954 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
955 continue;
956 }
957 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
958 continue;
959 }
960 flowPaths.add(flow);
961 }
962
963 if (flowPaths.isEmpty()) {
964 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800965 } else {
966 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
967 }
968
969 return flowPaths;
970 }
971
972 /**
973 * Get all installed flows by all installers for given data path endpoints.
974 *
975 * @param dataPathEndpoints the data path endpoints of the flows to get.
976 * @return the Flow Paths if found, otherwise null.
977 */
978 @Override
979 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
980 //
981 // TODO: The implementation below is not optimal:
982 // We fetch all flows, and then return only the subset that match
983 // the query conditions.
984 // We should use the appropriate Titan/Gremlin query to filter-out
985 // the flows as appropriate.
986 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700987 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
988 ArrayList<FlowPath> allFlows = getAllFlows();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800989
990 if (allFlows == null) {
991 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700992 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800993 }
994
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800995 for (FlowPath flow : allFlows) {
996 //
997 // TODO: String-based comparison is sub-optimal.
998 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800999 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001000 //
1001 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
1002 continue;
1003 }
1004 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
1005 continue;
1006 }
1007 flowPaths.add(flow);
1008 }
1009
1010 if (flowPaths.isEmpty()) {
1011 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001012 } else {
1013 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
1014 }
1015
1016 return flowPaths;
1017 }
1018
1019 /**
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001020 * Get summary of all installed flows by all installers in a given range
1021 *
1022 * @param flowId the data path endpoints of the flows to get.
1023 * @param maxFlows: the maximum number of flows to be returned
1024 * @return the Flow Paths if found, otherwise null.
1025 */
1026 @Override
1027 public ArrayList<FlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
1028 //
1029 // TODO: The implementation below is not optimal:
1030 // We fetch all flows, and then return only the subset that match
1031 // the query conditions.
1032 // We should use the appropriate Titan/Gremlin query to filter-out
1033 // the flows as appropriate.
1034 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001035 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1036
1037 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001038
1039 if (allFlows == null) {
1040 log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001041 return flowPaths;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001042 }
1043
1044 Collections.sort(allFlows);
1045
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001046 for (FlowPath flow : allFlows) {
1047
1048 // start from desired flowId
1049 if (flow.flowId().value() < flowId.value()) {
1050 continue;
1051 }
1052
1053 // Summarize by making null flow entry fields that are not relevant to report
1054 for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
1055 flowEntry.setFlowEntryActions(null);
1056 flowEntry.setFlowEntryMatch(null);
1057 }
1058
1059 flowPaths.add(flow);
1060 if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
1061 break;
1062 }
1063 }
1064
1065 if (flowPaths.isEmpty()) {
1066 log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001067 } else {
1068 log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
1069 }
1070
1071 return flowPaths;
1072 }
1073
1074 /**
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001075 * Get all installed flows by all installers.
1076 *
1077 * @return the Flow Paths if found, otherwise null.
1078 */
1079 @Override
1080 public ArrayList<FlowPath> getAllFlows() {
1081 Iterable<IFlowPath> flowPathsObj = null;
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001082 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001083
1084 try {
1085 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
1086 log.debug("Get all FlowPaths: found FlowPaths");
1087 } else {
1088 log.debug("Get all FlowPaths: no FlowPaths found");
1089 }
1090 } catch (Exception e) {
1091 // TODO: handle exceptions
1092 conn.endTx(Transaction.ROLLBACK);
1093 log.error(":getAllFlowPaths failed");
1094 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001095 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
1096 conn.endTx(Transaction.COMMIT);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001097 return flowPaths; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001098 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001099
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001100 for (IFlowPath flowObj : flowPathsObj) {
1101 //
1102 // Extract the Flow state
1103 //
1104 FlowPath flowPath = extractFlowPath(flowObj);
1105 flowPaths.add(flowPath);
1106 }
1107
1108 conn.endTx(Transaction.COMMIT);
1109
1110 return flowPaths;
1111 }
1112
1113 /**
1114 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
1115 *
1116 * @param flowObj the object to extract the Flow Path State from.
1117 * @return the extracted Flow Path State.
1118 */
1119 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001120 FlowPath flowPath = new FlowPath();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001121
1122 //
1123 // Extract the Flow state
1124 //
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001125 String flowIdStr = flowObj.getFlowId();
1126 String installerIdStr = flowObj.getInstallerId();
1127 String srcSwitchStr = flowObj.getSrcSwitch();
1128 Short srcPortStr = flowObj.getSrcPort();
1129 String dstSwitchStr = flowObj.getDstSwitch();
1130 Short dstPortStr = flowObj.getDstPort();
1131
1132 if ((flowIdStr == null) ||
1133 (installerIdStr == null) ||
1134 (srcSwitchStr == null) ||
1135 (srcPortStr == null) ||
1136 (dstSwitchStr == null) ||
1137 (dstPortStr == null)) {
1138 // TODO: A work-around, becauuse of some bogus database objects
1139 return null;
1140 }
1141
1142 flowPath.setFlowId(new FlowId(flowIdStr));
1143 flowPath.setInstallerId(new CallerId(installerIdStr));
1144 flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
1145 flowPath.dataPath().srcPort().setPort(new Port(srcPortStr));
1146 flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
1147 flowPath.dataPath().dstPort().setPort(new Port(dstPortStr));
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001148
1149 //
1150 // Extract all Flow Entries
1151 //
1152 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1153 for (IFlowEntry flowEntryObj : flowEntries) {
1154 FlowEntry flowEntry = new FlowEntry();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001155 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1156 String switchDpidStr = flowEntryObj.getSwitchDpid();
1157 String userState = flowEntryObj.getUserState();
1158 String switchState = flowEntryObj.getSwitchState();
1159
1160 if ((flowEntryIdStr == null) ||
1161 (switchDpidStr == null) ||
1162 (userState == null) ||
1163 (switchState == null)) {
1164 // TODO: A work-around, becauuse of some bogus database objects
1165 continue;
1166 }
1167 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
1168 flowEntry.setDpid(new Dpid(switchDpidStr));
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -07001169
1170 //
1171 // Extract the match conditions
1172 //
1173 FlowEntryMatch match = new FlowEntryMatch();
1174 Short matchInPort = flowEntryObj.getMatchInPort();
1175 if (matchInPort != null)
1176 match.enableInPort(new Port(matchInPort));
1177 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1178 if (matchEthernetFrameType != null)
1179 match.enableEthernetFrameType(matchEthernetFrameType);
1180 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1181 if (matchSrcIPv4Net != null)
1182 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1183 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1184 if (matchDstIPv4Net != null)
1185 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1186 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1187 if (matchSrcMac != null)
1188 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1189 String matchDstMac = flowEntryObj.getMatchDstMac();
1190 if (matchDstMac != null)
1191 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1192 flowEntry.setFlowEntryMatch(match);
1193
1194 //
1195 // Extract the actions
1196 //
1197 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
1198 Short actionOutputPort = flowEntryObj.getActionOutput();
1199 if (actionOutputPort != null) {
1200 FlowEntryAction action = new FlowEntryAction();
1201 action.setActionOutput(new Port(actionOutputPort));
1202 actions.add(action);
1203 }
1204 flowEntry.setFlowEntryActions(actions);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001205 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001206 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
1207 //
Pavlin Radoslavovede97582013-03-08 18:57:28 -08001208 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001209 // and FlowEntryErrorState.
1210 //
1211 flowPath.dataPath().flowEntries().add(flowEntry);
1212 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001213
1214 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001215 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001216
1217 /**
1218 * Add and maintain a shortest-path flow.
1219 *
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001220 * NOTE: The Flow Path argument does NOT contain all flow entries.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001221 * Instead, it contains a single dummy flow entry that is used to
1222 * store the matching condition(s).
1223 * That entry is replaced by the appropriate entries from the
1224 * internally performed shortest-path computation.
1225 *
1226 * @param flowPath the Flow Path with the endpoints and the match
1227 * conditions to install.
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001228 * @return the added shortest-path flow on success, otherwise null.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001229 */
1230 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001231 public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001232 //
1233 // Do the shortest path computation
1234 //
1235 DataPath dataPath =
1236 topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
1237 flowPath.dataPath().dstPort());
1238 if (dataPath == null)
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001239 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001240
1241 FlowEntryMatch userFlowEntryMatch = null;
1242 if (! flowPath.dataPath().flowEntries().isEmpty()) {
1243 userFlowEntryMatch = flowPath.dataPath().flowEntries().get(0).flowEntryMatch();
1244 }
1245
1246 //
1247 // Set the incoming port matching and the outgoing port output
1248 // actions for each flow entry.
1249 //
1250 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1251 // Set the incoming port matching
1252 FlowEntryMatch flowEntryMatch = null;
1253 if (userFlowEntryMatch != null)
1254 flowEntryMatch = new FlowEntryMatch(userFlowEntryMatch);
1255 else
1256 flowEntryMatch = new FlowEntryMatch();
1257 flowEntry.setFlowEntryMatch(flowEntryMatch);
1258 flowEntryMatch.enableInPort(flowEntry.inPort());
1259
1260 // Set the outgoing port output action
1261 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1262 if (flowEntryActions == null) {
1263 flowEntryActions = new ArrayList<FlowEntryAction>();
1264 flowEntry.setFlowEntryActions(flowEntryActions);
1265 }
1266 FlowEntryAction flowEntryAction = new FlowEntryAction();
1267 flowEntryAction.setActionOutput(flowEntry.outPort());
1268 flowEntryActions.add(flowEntryAction);
1269 }
1270
1271 //
1272 // Prepare the computed Flow Path
1273 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001274 FlowPath computedFlowPath = new FlowPath();
1275 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1276 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1277 computedFlowPath.setDataPath(dataPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001278
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001279 FlowId flowId = new FlowId();
1280 if (! addFlow(computedFlowPath, flowId))
1281 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001282
1283 // TODO: Mark the flow for maintenance purpose
1284
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001285 return (computedFlowPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001286 }
1287
1288 /**
1289 * Create a Flow from port to port.
1290 *
1291 * TODO: We don't need it for now.
1292 *
1293 * @param src_port the source port.
1294 * @param dest_port the destination port.
1295 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001296 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001297 public void createFlow(IPortObject src_port, IPortObject dest_port) {
1298 // TODO: We don't need it for now.
1299 }
1300
1301 /**
1302 * Get all Flows matching a source and a destination port.
1303 *
1304 * TODO: Pankaj might be implementing it later.
1305 *
1306 * @param src_port the source port to match.
1307 * @param dest_port the destination port to match.
1308 * @return all flows matching the source and the destination port.
1309 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001310 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001311 public Iterable<FlowPath> getFlows(IPortObject src_port,
1312 IPortObject dest_port) {
1313 // TODO: Pankaj might be implementing it later.
1314 return null;
1315 }
1316
1317 /**
1318 * Get all Flows going out from a port.
1319 *
1320 * TODO: We need it now: Pankaj
1321 *
1322 * @param port the port to match.
1323 * @return the list of flows that are going out from the port.
1324 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001325 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001326 public Iterable<FlowPath> getFlows(IPortObject port) {
1327 // TODO: We need it now: Pankaj
1328 return null;
1329 }
1330
1331 /**
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001332 * Reconcile all flows on inactive switch port.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001333 *
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001334 * @param portObject the port that has become inactive.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001335 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001336 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001337 public void reconcileFlows(IPortObject portObject) {
1338 Iterable<IFlowEntry> inFlowEntries = portObject.getInFlowEntries();
1339 Iterable<IFlowEntry> outFlowEntries = portObject.getOutFlowEntries();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001340
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001341 //
1342 // Collect all affected Flow IDs from the affected flow entries
1343 //
1344 HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
1345 for (IFlowEntry flowEntryObj: inFlowEntries) {
1346 IFlowPath flowObj = flowEntryObj.getFlow();
1347 if (flowObj != null)
1348 flowObjSet.add(flowObj);
1349 }
1350 for (IFlowEntry flowEntryObj: outFlowEntries) {
1351 IFlowPath flowObj = flowEntryObj.getFlow();
1352 if (flowObj != null)
1353 flowObjSet.add(flowObj);
1354 }
1355 // conn.endTx(Transaction.COMMIT);
1356
1357 //
1358 // Remove the old Flow Entries, and add the new Flow Entries
1359 //
1360 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
1361 for (IFlowPath flowObj : flowObjSet) {
1362 FlowPath flowPath = extractFlowPath(flowObj);
1363 if (flowPath == null)
1364 continue;
1365
1366 //
1367 // Remove my Flow Entries from the Network MAP
1368 //
1369 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1370 for (IFlowEntry flowEntryObj : flowEntries) {
1371 String dpidStr = flowEntryObj.getSwitchDpid();
1372 if (dpidStr == null)
1373 continue;
1374 Dpid dpid = new Dpid(dpidStr);
1375 IOFSwitch mySwitch = mySwitches.get(dpid.value());
1376 if (mySwitch == null)
1377 continue; // Ignore the entry: not my switch
1378 flowObj.removeFlowEntry(flowEntryObj);
1379 conn.utils().removeFlowEntry(conn, flowEntryObj);
1380 }
1381
1382 //
1383 // Delete the flow entries from the switches
1384 //
1385 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
1386 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001387 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1388 if (mySwitch == null) {
1389 // Not my switch
1390 installRemoteFlowEntry(flowEntry);
1391 } else {
1392 installFlowEntry(mySwitch, flowEntry);
1393 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001394 }
1395
1396 //
1397 // Calculate the new shortest path and install it in the
1398 // Network MAP.
1399 //
1400 FlowPath addedFlowPath = addAndMaintainShortestPathFlow(flowPath);
1401 if (addedFlowPath == null) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001402 log.error("Cannot add Shortest Path Flow from {} to {}: path not found?",
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001403 flowPath.dataPath().srcPort().toString(),
1404 flowPath.dataPath().dstPort().toString());
1405 continue;
1406 }
1407
1408 //
1409 // Add the flow entries to the switches
1410 //
1411 for (FlowEntry flowEntry : addedFlowPath.dataPath().flowEntries()) {
1412 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001413 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1414 if (mySwitch == null) {
1415 // Not my switch
1416 installRemoteFlowEntry(flowEntry);
1417 continue;
1418 }
1419
1420 IFlowEntry flowEntryObj =
1421 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId());
1422 if (flowEntryObj == null) {
1423 //
1424 // TODO: Remove the "new Object[] wrapper in the statement
1425 // below after the SLF4J logger is upgraded to
1426 // Version 1.7.5
1427 //
1428 log.error("Cannot add Flow Entry to switch {} for Path Flow from {} to {} : Flow Entry not in the Network MAP",
1429 new Object[] {
1430 flowEntry.dpid(),
1431 flowPath.dataPath().srcPort(),
1432 flowPath.dataPath().dstPort()
1433 });
1434 continue;
1435 }
1436 // Update the Flow Entry Switch State in the Network MAP
1437 if (installFlowEntry(mySwitch, flowEntry)) {
1438 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1439 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001440 }
1441 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001442 }
1443
1444 /**
1445 * Reconcile all flows between a source and a destination port.
1446 *
1447 * TODO: We don't need it for now.
1448 *
1449 * @param src_port the source port.
1450 * @param dest_port the destination port.
1451 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001452 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001453 public void reconcileFlow(IPortObject src_port, IPortObject dest_port) {
1454 // TODO: We don't need it for now.
1455 }
1456
1457 /**
1458 * Compute the shortest path between a source and a destination ports.
1459 *
1460 * @param src_port the source port.
1461 * @param dest_port the destination port.
1462 * @return the computed shortest path between the source and the
1463 * destination ports. The flow entries in the path itself would
1464 * contain the incoming port matching and the outgoing port output
1465 * actions set. However, the path itself will NOT have the Flow ID,
1466 * Installer ID, and any additional matching conditions for the
1467 * flow entries (e.g., source or destination MAC address, etc).
1468 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001469 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001470 public FlowPath computeFlowPath(IPortObject src_port,
1471 IPortObject dest_port) {
1472 //
1473 // Prepare the arguments
1474 //
1475 String dpidStr = src_port.getSwitch().getDPID();
1476 Dpid srcDpid = new Dpid(dpidStr);
1477 Port srcPort = new Port(src_port.getNumber());
1478
1479 dpidStr = dest_port.getSwitch().getDPID();
1480 Dpid dstDpid = new Dpid(dpidStr);
1481 Port dstPort = new Port(dest_port.getNumber());
1482
1483 SwitchPort src = new SwitchPort(srcDpid, srcPort);
1484 SwitchPort dst = new SwitchPort(dstDpid, dstPort);
1485
1486 //
1487 // Do the shortest path computation
1488 //
1489 DataPath dataPath = topoRouteService.getShortestPath(src, dst);
1490 if (dataPath == null)
1491 return null;
1492
1493 //
1494 // Set the incoming port matching and the outgoing port output
1495 // actions for each flow entry.
1496 //
1497 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1498 // Set the incoming port matching
1499 FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
1500 if (flowEntryMatch == null) {
1501 flowEntryMatch = new FlowEntryMatch();
1502 flowEntry.setFlowEntryMatch(flowEntryMatch);
1503 }
1504 flowEntryMatch.enableInPort(flowEntry.inPort());
1505
1506 // Set the outgoing port output action
1507 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1508 if (flowEntryActions == null) {
1509 flowEntryActions = new ArrayList<FlowEntryAction>();
1510 flowEntry.setFlowEntryActions(flowEntryActions);
1511 }
1512 FlowEntryAction flowEntryAction = new FlowEntryAction();
1513 flowEntryAction.setActionOutput(flowEntry.outPort());
1514 flowEntryActions.add(flowEntryAction);
1515 }
1516
1517 //
1518 // Prepare the return result
1519 //
1520 FlowPath flowPath = new FlowPath();
1521 flowPath.setDataPath(dataPath);
1522
1523 return flowPath;
1524 }
1525
1526 /**
1527 * Get all Flow Entries of a Flow.
1528 *
1529 * @param flow the flow whose flow entries should be returned.
1530 * @return the flow entries of the flow.
1531 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001532 @Override
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001533 public Iterable<FlowEntry> getFlowEntries(FlowPath flow) {
1534 return flow.dataPath().flowEntries();
1535 }
1536
1537 /**
1538 * Install a Flow Entry on a switch.
1539 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001540 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001541 * @param flowEntry the flow entry to install.
1542 * @return true on success, otherwise false.
1543 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001544 @Override
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001545 public boolean installFlowEntry(IOFSwitch mySwitch, FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001546 //
1547 // Create the OpenFlow Flow Modification Entry to push
1548 //
1549 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1550 .getMessage(OFType.FLOW_MOD);
1551 long cookie = flowEntry.flowEntryId().value();
1552
1553 short flowModCommand = OFFlowMod.OFPFC_ADD;
1554 if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
1555 flowModCommand = OFFlowMod.OFPFC_ADD;
1556 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
1557 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1558 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
1559 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1560 } else {
1561 // Unknown user state. Ignore the entry
1562 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1563 flowEntry.flowEntryId().toString(),
1564 flowEntry.flowEntryUserState());
1565 return false;
1566 }
1567
1568 //
1569 // Fetch the match conditions
1570 //
1571 OFMatch match = new OFMatch();
1572 match.setWildcards(OFMatch.OFPFW_ALL);
1573 Port matchInPort = flowEntry.flowEntryMatch().inPort();
1574 if (matchInPort != null) {
1575 match.setInputPort(matchInPort.value());
1576 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1577 }
1578 Short matchEthernetFrameType =
1579 flowEntry.flowEntryMatch().ethernetFrameType();
1580 if (matchEthernetFrameType != null) {
1581 match.setDataLayerType(matchEthernetFrameType);
1582 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1583 }
1584 IPv4Net matchSrcIPv4Net = flowEntry.flowEntryMatch().srcIPv4Net();
1585 if (matchSrcIPv4Net != null) {
1586 match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
1587 }
1588 IPv4Net matchDstIPv4Net = flowEntry.flowEntryMatch().dstIPv4Net();
1589 if (matchDstIPv4Net != null) {
1590 match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
1591 }
1592 MACAddress matchSrcMac = flowEntry.flowEntryMatch().srcMac();
1593 if (matchSrcMac != null) {
1594 match.setDataLayerSource(matchSrcMac.toString());
1595 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1596 }
1597 MACAddress matchDstMac = flowEntry.flowEntryMatch().dstMac();
1598 if (matchDstMac != null) {
1599 match.setDataLayerDestination(matchDstMac.toString());
1600 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1601 }
1602
1603 //
1604 // Fetch the actions
1605 //
1606 // TODO: For now we support only the "OUTPUT" actions.
1607 //
1608 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1609 List<OFAction> actions = new ArrayList<OFAction>();
1610 ArrayList<FlowEntryAction> flowEntryActions =
1611 flowEntry.flowEntryActions();
1612 for (FlowEntryAction flowEntryAction : flowEntryActions) {
1613 FlowEntryAction.ActionOutput actionOutput =
1614 flowEntryAction.actionOutput();
1615 if (actionOutput != null) {
1616 short actionOutputPort = actionOutput.port().value();
1617 OFActionOutput action = new OFActionOutput();
1618 // XXX: The max length is hard-coded for now
1619 action.setMaxLength((short)0xffff);
1620 action.setPort(actionOutputPort);
1621 actions.add(action);
1622 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1623 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1624 fm.setOutPort(actionOutputPort);
1625 }
1626 }
1627 }
1628
1629 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1630 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1631 .setPriority(PRIORITY_DEFAULT)
1632 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1633 .setCookie(cookie)
1634 .setCommand(flowModCommand)
1635 .setMatch(match)
1636 .setActions(actions)
1637 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1638
1639 //
1640 // TODO: Set the following flag
1641 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1642 // See method ForwardingBase::pushRoute()
1643 //
1644
1645 //
1646 // Write the message to the switch
1647 //
1648 try {
1649 messageDamper.write(mySwitch, fm, null);
1650 mySwitch.flush();
1651 } catch (IOException e) {
1652 log.error("Failure writing flow mod from network map", e);
1653 return false;
1654 }
1655 return true;
1656 }
1657
1658 /**
1659 * Remove a Flow Entry from a switch.
1660 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001661 * @param mySwitch the switch to remove the Flow Entry from.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001662 * @param flowEntry the flow entry to remove.
1663 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001664 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001665 @Override
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001666 public boolean removeFlowEntry(IOFSwitch mySwitch, FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001667 //
1668 // The installFlowEntry() method implements both installation
1669 // and removal of flow entries.
1670 //
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001671 return (installFlowEntry(mySwitch, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001672 }
1673
1674 /**
1675 * Install a Flow Entry on a remote controller.
1676 *
1677 * TODO: We need it now: Jono
1678 * - For now it will make a REST call to the remote controller.
1679 * - Internally, it needs to know the name of the remote controller.
1680 *
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001681 * @param flowEntry the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001682 * @return true on success, otherwise false.
1683 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001684 @Override
1685 public boolean installRemoteFlowEntry(FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001686 // TODO: We need it now: Jono
1687 // - For now it will make a REST call to the remote controller.
1688 // - Internally, it needs to know the name of the remote controller.
1689 return true;
1690 }
1691
1692 /**
1693 * Remove a flow entry on a remote controller.
1694 *
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001695 * @param flowEntry the flow entry to remove.
1696 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001697 */
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001698 @Override
1699 public boolean removeRemoteFlowEntry(FlowEntry flowEntry) {
1700 //
1701 // The installRemoteFlowEntry() method implements both installation
1702 // and removal of flow entries.
1703 //
1704 return (installRemoteFlowEntry(flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001705 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001706}