blob: d64476d14ef1bc06a97ded1c153451e751f0260f [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 Radoslavovb6f53542013-03-01 16:02:14 -08008import java.util.List;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08009import java.util.Map;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070010import java.util.TreeMap;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080011import java.util.concurrent.Executors;
12import java.util.concurrent.ScheduledExecutorService;
13import java.util.concurrent.ScheduledFuture;
14import java.util.concurrent.TimeUnit;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080015
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080016import net.floodlightcontroller.core.IFloodlightProviderService;
17import net.floodlightcontroller.core.INetMapStorage;
18import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
19import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
20import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080021import net.floodlightcontroller.core.module.FloodlightModuleContext;
22import net.floodlightcontroller.core.module.FloodlightModuleException;
23import net.floodlightcontroller.core.module.IFloodlightModule;
24import net.floodlightcontroller.core.module.IFloodlightService;
25import net.floodlightcontroller.flowcache.IFlowService;
26import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
27import net.floodlightcontroller.restserver.IRestApiService;
28import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080029import net.floodlightcontroller.util.DataPath;
30import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080031import net.floodlightcontroller.util.DataPathEndpoints;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080032import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070033import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080034import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070035import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080036import net.floodlightcontroller.util.FlowEntrySwitchState;
37import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080038import net.floodlightcontroller.util.FlowId;
39import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070040import net.floodlightcontroller.util.IPv4Net;
41import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080042import net.floodlightcontroller.util.OFMessageDamper;
43import net.floodlightcontroller.util.Port;
44import net.onrc.onos.util.GraphDBConnection;
45import net.onrc.onos.util.GraphDBConnection.Transaction;
46
47import org.openflow.protocol.OFFlowMod;
48import org.openflow.protocol.OFMatch;
49import org.openflow.protocol.OFPacketOut;
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070050import org.openflow.protocol.OFPort;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080051import org.openflow.protocol.OFType;
52import org.openflow.protocol.action.OFAction;
53import org.openflow.protocol.action.OFActionOutput;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080054
55import org.slf4j.Logger;
56import org.slf4j.LoggerFactory;
57
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080058public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
59
60 public GraphDBConnection conn;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080061
62 protected IRestApiService restApi;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080063 protected IFloodlightProviderService floodlightProvider;
64
65 protected OFMessageDamper messageDamper;
66
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -070067 //
68 // TODO: Values copied from elsewhere (class LearningSwitch).
69 // The local copy should go away!
70 //
71 protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
72 protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
73 public static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0; // infinity
74 public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
75 public static final short PRIORITY_DEFAULT = 100;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080076
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070077 private static long nextFlowEntryId = 1;
78
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080079 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080080 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
81
82 // The periodic task(s)
83 private final ScheduledExecutorService scheduler =
84 Executors.newScheduledThreadPool(1);
85 final Runnable reader = new Runnable() {
86 public void run() {
87 // log.debug("Reading Flow Entries from the Network Map...");
88 if (floodlightProvider == null) {
89 log.debug("FloodlightProvider service not found!");
90 return;
91 }
92
93 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
94
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070095 Map<Long, IFlowEntry> myFlowEntries = new TreeMap<Long, IFlowEntry>();
96
97 //
98 // Fetch all Flow Entries and select only my Flow Entries
99 //
100 Iterable<IFlowEntry> allFlowEntries = conn.utils().getAllFlowEntries(conn);
101 for (IFlowEntry flowEntryObj : allFlowEntries) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800102 FlowEntryId flowEntryId =
103 new FlowEntryId(flowEntryObj.getFlowEntryId());
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700104 String userState = flowEntryObj.getUserState();
105 String switchState = flowEntryObj.getSwitchState();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800106
Pavlin Radoslavov79a67c12013-03-15 21:05:53 -0700107 log.debug("Found Flow Entry {}: {}",
108 flowEntryId.toString(),
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700109 "User State: " + userState +
110 " Switch State: " + switchState);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800111
112 if (! switchState.equals("FE_SWITCH_NOT_UPDATED")) {
113 // Ignore the entry: nothing to do
114 continue;
115 }
116
117 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
118 IOFSwitch mySwitch = mySwitches.get(dpid.value());
119 if (mySwitch == null) {
Pavlin Radoslavov79a67c12013-03-15 21:05:53 -0700120 log.debug("Flow Entry ignored: not my switch (FlowEntryId = {} DPID = {})", flowEntryId.toString(), dpid.toString());
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800121 continue;
122 }
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700123 myFlowEntries.put(flowEntryId.value(), flowEntryObj);
124 }
125
126 //
127 // Process my Flow Entries
128 //
129 for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
130 IFlowEntry flowEntryObj = entry.getValue();
131
132 //
133 // TODO: Eliminate the re-fetching of flowEntryId,
134 // userState, switchState, and dpid from the flowEntryObj.
135 //
136 FlowEntryId flowEntryId =
137 new FlowEntryId(flowEntryObj.getFlowEntryId());
138 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
139 String userState = flowEntryObj.getUserState();
140 String switchState = flowEntryObj.getSwitchState();
141 IOFSwitch mySwitch = mySwitches.get(dpid.value());
142 if (mySwitch == null) {
143 log.debug("Flow Entry ignored: not my switch");
144 continue;
145 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800146
147 //
148 // Create the Open Flow Flow Modification Entry to push
149 //
150 OFFlowMod fm =
151 (OFFlowMod) floodlightProvider.getOFMessageFactory()
152 .getMessage(OFType.FLOW_MOD);
153 long cookie = flowEntryId.value();
154
155 short flowModCommand = OFFlowMod.OFPFC_ADD;
156 if (userState.equals("FE_USER_ADD")) {
157 flowModCommand = OFFlowMod.OFPFC_ADD;
158 } else if (userState.equals("FE_USER_MODIFY")) {
159 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
160 } else if (userState.equals("FE_USER_DELETE")) {
161 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
162 } else {
163 // Unknown user state. Ignore the entry
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700164 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
165 flowEntryId.toString(), userState);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800166 continue;
167 }
168
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700169 //
170 // Fetch the match conditions
171 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800172 OFMatch match = new OFMatch();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700173 match.setWildcards(OFMatch.OFPFW_ALL);
174 Short matchInPort = flowEntryObj.getMatchInPort();
175 if (matchInPort != null) {
176 match.setInputPort(matchInPort);
177 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
178 }
179 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
180 if (matchEthernetFrameType != null) {
181 match.setDataLayerType(matchEthernetFrameType);
182 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
183 }
184 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
185 if (matchSrcIPv4Net != null) {
186 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
187 }
188 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
189 if (matchDstIPv4Net != null) {
190 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
191 }
192 String matchSrcMac = flowEntryObj.getMatchSrcMac();
193 if (matchSrcMac != null) {
194 match.setDataLayerSource(matchSrcMac);
195 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
196 }
197 String matchDstMac = flowEntryObj.getMatchDstMac();
198 if (matchDstMac != null) {
199 match.setDataLayerDestination(matchDstMac);
200 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
201 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700202
203 //
204 // Fetch the actions
205 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800206 List<OFAction> actions = new ArrayList<OFAction>();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700207 Short actionOutputPort = flowEntryObj.getActionOutput();
208 if (actionOutputPort != null) {
209 OFActionOutput action = new OFActionOutput();
210 // XXX: The max length is hard-coded for now
211 action.setMaxLength((short)0xffff);
212 action.setPort(actionOutputPort);
213 actions.add(action);
214 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800215
216 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
217 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700218 .setPriority(PRIORITY_DEFAULT)
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800219 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
220 .setCookie(cookie)
221 .setCommand(flowModCommand)
222 .setMatch(match)
223 .setActions(actions)
224 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700225 fm.setOutPort(OFPort.OFPP_NONE.getValue());
226 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
227 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
228 if (actionOutputPort != null)
229 fm.setOutPort(actionOutputPort);
230 }
231
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800232 //
233 // TODO: Set the following flag
234 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
235 // See method ForwardingBase::pushRoute()
236 //
237 try {
238 messageDamper.write(mySwitch, fm, null);
239 mySwitch.flush();
240 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
241 if (userState.equals("FE_USER_DELETE")) {
242 // Delete the entry
243 IFlowPath flowObj = null;
244 flowObj = conn.utils().getFlowPathByFlowEntry(conn,
245 flowEntryObj);
246 if (flowObj != null)
247 log.debug("Found FlowPath to be deleted");
248 else
249 log.debug("Did not find FlowPath to be deleted");
250 flowObj.removeFlowEntry(flowEntryObj);
251 conn.utils().removeFlowEntry(conn, flowEntryObj);
252
253 // Test whether the last flow entry
254 Iterable<IFlowEntry> tmpflowEntries =
255 flowObj.getFlowEntries();
256 boolean found = false;
257 for (IFlowEntry tmpflowEntryObj : tmpflowEntries) {
258 found = true;
259 break;
260 }
261 if (! found) {
262 // Remove the Flow Path as well
263 conn.utils().removeFlowPath(conn, flowObj);
264 }
265 }
266 } catch (IOException e) {
267 log.error("Failure writing flow mod from network map", e);
268 }
269 }
270 conn.endTx(Transaction.COMMIT);
271 }
272 };
273 final ScheduledFuture<?> readerHandle =
274 scheduler.scheduleAtFixedRate(reader, 3, 3, TimeUnit.SECONDS);
275
276 @Override
277 public void init(String conf) {
278 conn = GraphDBConnection.getInstance(conf);
279 }
280
281 public void finalize() {
282 close();
283 }
284
285 @Override
286 public void close() {
287 conn.close();
288 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800289
290 @Override
291 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
292 Collection<Class<? extends IFloodlightService>> l =
293 new ArrayList<Class<? extends IFloodlightService>>();
294 l.add(IFlowService.class);
295 return l;
296 }
297
298 @Override
299 public Map<Class<? extends IFloodlightService>, IFloodlightService>
300 getServiceImpls() {
301 Map<Class<? extends IFloodlightService>,
302 IFloodlightService> m =
303 new HashMap<Class<? extends IFloodlightService>,
304 IFloodlightService>();
305 m.put(IFlowService.class, this);
306 return m;
307 }
308
309 @Override
310 public Collection<Class<? extends IFloodlightService>>
311 getModuleDependencies() {
312 Collection<Class<? extends IFloodlightService>> l =
313 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800314 l.add(IFloodlightProviderService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800315 l.add(IRestApiService.class);
316 return l;
317 }
318
319 @Override
320 public void init(FloodlightModuleContext context)
321 throws FloodlightModuleException {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800322 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800323 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800324 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
325 EnumSet.of(OFType.FLOW_MOD),
326 OFMESSAGE_DAMPER_TIMEOUT);
327 // TODO: An ugly hack!
328 String conf = "/tmp/cassandra.titan";
329 this.init(conf);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800330 }
331
332 @Override
333 public void startUp(FloodlightModuleContext context) {
334 restApi.addRestletRoutable(new FlowWebRoutable());
335 }
336
337 /**
338 * Add a flow.
339 *
340 * Internally, ONOS will automatically register the installer for
341 * receiving Flow Path Notifications for that path.
342 *
343 * @param flowPath the Flow Path to install.
344 * @param flowId the return-by-reference Flow ID as assigned internally.
345 * @return true on success, otherwise false.
346 */
347 @Override
348 public boolean addFlow(FlowPath flowPath, FlowId flowId) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800349
350 //
351 // Assign the FlowEntry IDs
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700352 // Right now every new flow entry gets a new flow entry ID
353 // TODO: This needs to be redesigned!
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800354 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800355 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700356 long id = nextFlowEntryId++;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800357 flowEntry.setFlowEntryId(new FlowEntryId(id));
358 }
359
360 IFlowPath flowObj = null;
361 try {
362 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
363 != null) {
364 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
365 flowPath.flowId().toString());
366 } else {
367 flowObj = conn.utils().newFlowPath(conn);
368 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
369 flowPath.flowId().toString());
370 }
371 } catch (Exception e) {
372 // TODO: handle exceptions
373 conn.endTx(Transaction.ROLLBACK);
374 log.error(":addFlow FlowId:{} failed",
375 flowPath.flowId().toString());
376 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700377 if (flowObj == null) {
378 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800379 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700380 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800381
382 //
383 // Set the Flow key:
384 // - flowId
385 //
386 flowObj.setFlowId(flowPath.flowId().toString());
387 flowObj.setType("flow");
388
389 //
390 // Set the Flow attributes:
391 // - flowPath.installerId()
392 // - flowPath.dataPath().srcPort()
393 // - flowPath.dataPath().dstPort()
394 //
395 flowObj.setInstallerId(flowPath.installerId().toString());
396 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
397 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
398 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
399 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
400
401 // Flow edges:
402 // HeadFE
403
404
405 //
406 // Flow Entries:
407 // flowPath.dataPath().flowEntries()
408 //
409 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
410 IFlowEntry flowEntryObj = null;
411 boolean found = false;
412 try {
413 if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
414 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
415 flowEntry.flowEntryId().toString());
416 found = true;
417 } else {
418 flowEntryObj = conn.utils().newFlowEntry(conn);
419 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
420 flowEntry.flowEntryId().toString());
421 }
422 } catch (Exception e) {
423 // TODO: handle exceptions
424 conn.endTx(Transaction.ROLLBACK);
425 log.error(":addFlow FlowEntryId:{} failed",
426 flowEntry.flowEntryId().toString());
427 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700428 if (flowEntryObj == null) {
429 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800430 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700431 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800432
433 //
434 // Set the Flow Entry key:
435 // - flowEntry.flowEntryId()
436 //
437 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
438 flowEntryObj.setType("flow_entry");
439
440 //
441 // Set the Flow Entry attributes:
442 // - flowEntry.flowEntryMatch()
443 // - flowEntry.flowEntryActions()
444 // - flowEntry.dpid()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800445 // - flowEntry.flowEntryUserState()
446 // - flowEntry.flowEntrySwitchState()
447 // - flowEntry.flowEntryErrorState()
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700448 // - flowEntry.matchInPort()
449 // - flowEntry.matchEthernetFrameType()
450 // - flowEntry.matchSrcIPv4Net()
451 // - flowEntry.matchDstIPv4Net()
452 // - flowEntry.matchSrcMac()
453 // - flowEntry.matchDstMac()
454 // - flowEntry.actionOutput()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800455 //
456 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700457 if (flowEntry.flowEntryMatch().matchInPort())
458 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
459 if (flowEntry.flowEntryMatch().matchEthernetFrameType())
460 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
461 if (flowEntry.flowEntryMatch().matchSrcIPv4Net())
462 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
463 if (flowEntry.flowEntryMatch().matchDstIPv4Net())
464 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
465 if (flowEntry.flowEntryMatch().matchSrcMac())
466 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
467 if (flowEntry.flowEntryMatch().matchDstMac())
468 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
469
470 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
471 if (fa.actionOutput() != null)
472 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
473 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800474 // TODO: Hacks with hard-coded state names!
475 if (found)
476 flowEntryObj.setUserState("FE_USER_MODIFY");
477 else
478 flowEntryObj.setUserState("FE_USER_ADD");
479 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
480 //
Pavlin Radoslavovede97582013-03-08 18:57:28 -0800481 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800482 // and FlowEntryErrorState.
483 //
484
485 // Flow Entries edges:
486 // Flow
487 // NextFE
488 // InPort
489 // OutPort
490 // Switch
491 if (! found)
492 flowObj.addFlowEntry(flowEntryObj);
493 }
494 conn.endTx(Transaction.COMMIT);
495
496 //
497 // TODO: We need a proper Flow ID allocation mechanism.
498 //
499 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700500
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800501 return true;
502 }
503
504 /**
505 * Delete a previously added flow.
506 *
507 * @param flowId the Flow ID of the flow to delete.
508 * @return true on success, otherwise false.
509 */
510 @Override
511 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800512 IFlowPath flowObj = null;
513 //
514 // We just mark the entries for deletion,
515 // and let the switches remove each individual entry after
516 // it has been removed from the switches.
517 //
518 try {
519 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
520 != null) {
521 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
522 flowId.toString());
523 } else {
524 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
525 flowId.toString());
526 }
527 } catch (Exception e) {
528 // TODO: handle exceptions
529 conn.endTx(Transaction.ROLLBACK);
530 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
531 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700532 if (flowObj == null) {
533 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800534 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700535 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800536
537 //
538 // Find and mark for deletion all Flow Entries
539 //
540 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
541 boolean empty = true; // TODO: an ugly hack
542 for (IFlowEntry flowEntryObj : flowEntries) {
543 empty = false;
544 // flowObj.removeFlowEntry(flowEntryObj);
545 // conn.utils().removeFlowEntry(conn, flowEntryObj);
546 flowEntryObj.setUserState("FE_USER_DELETE");
547 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
548 }
549 // Remove from the database empty flows
550 if (empty)
551 conn.utils().removeFlowPath(conn, flowObj);
552 conn.endTx(Transaction.COMMIT);
553
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800554 return true;
555 }
556
557 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700558 * Clear the state for a previously added flow.
559 *
560 * @param flowId the Flow ID of the flow to clear.
561 * @return true on success, otherwise false.
562 */
563 @Override
564 public boolean clearFlow(FlowId flowId) {
565 IFlowPath flowObj = null;
566 try {
567 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
568 != null) {
569 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
570 flowId.toString());
571 } else {
572 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
573 flowId.toString());
574 }
575 } catch (Exception e) {
576 // TODO: handle exceptions
577 conn.endTx(Transaction.ROLLBACK);
578 log.error(":clearFlow FlowId:{} failed", flowId.toString());
579 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700580 if (flowObj == null) {
581 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700582 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700583 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700584
585 //
586 // Remove all Flow Entries
587 //
588 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
589 for (IFlowEntry flowEntryObj : flowEntries) {
590 flowObj.removeFlowEntry(flowEntryObj);
591 conn.utils().removeFlowEntry(conn, flowEntryObj);
592 }
593 // Remove the Flow itself
594 conn.utils().removeFlowPath(conn, flowObj);
595 conn.endTx(Transaction.COMMIT);
596
597 return true;
598 }
599
600 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800601 * Get a previously added flow.
602 *
603 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800604 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800605 */
606 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800607 public FlowPath getFlow(FlowId flowId) {
608 IFlowPath flowObj = null;
609 try {
610 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
611 != null) {
612 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
613 flowId.toString());
614 } else {
615 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
616 flowId.toString());
617 }
618 } catch (Exception e) {
619 // TODO: handle exceptions
620 conn.endTx(Transaction.ROLLBACK);
621 log.error(":getFlow FlowId:{} failed", flowId.toString());
622 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700623 if (flowObj == null) {
624 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800625 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700626 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800627
628 //
629 // Extract the Flow state
630 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800631 FlowPath flowPath = extractFlowPath(flowObj);
632 conn.endTx(Transaction.COMMIT);
633
634 return flowPath;
635 }
636
637 /**
638 * Get all previously added flows by a specific installer for a given
639 * data path endpoints.
640 *
641 * @param installerId the Caller ID of the installer of the flow to get.
642 * @param dataPathEndpoints the data path endpoints of the flow to get.
643 * @return the Flow Paths if found, otherwise null.
644 */
645 @Override
646 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
647 DataPathEndpoints dataPathEndpoints) {
648 //
649 // TODO: The implementation below is not optimal:
650 // We fetch all flows, and then return only the subset that match
651 // the query conditions.
652 // We should use the appropriate Titan/Gremlin query to filter-out
653 // the flows as appropriate.
654 //
655 ArrayList<FlowPath> allFlows = getAllFlows();
656
657 if (allFlows == null) {
658 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
659 return null;
660 }
661
662 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
663 for (FlowPath flow : allFlows) {
664 //
665 // TODO: String-based comparison is sub-optimal.
666 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800667 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800668 //
669 if (! flow.installerId().toString().equals(installerId.toString()))
670 continue;
671 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
672 continue;
673 }
674 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
675 continue;
676 }
677 flowPaths.add(flow);
678 }
679
680 if (flowPaths.isEmpty()) {
681 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
682 flowPaths = null;
683 } else {
684 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
685 }
686
687 return flowPaths;
688 }
689
690 /**
691 * Get all installed flows by all installers for given data path endpoints.
692 *
693 * @param dataPathEndpoints the data path endpoints of the flows to get.
694 * @return the Flow Paths if found, otherwise null.
695 */
696 @Override
697 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
698 //
699 // TODO: The implementation below is not optimal:
700 // We fetch all flows, and then return only the subset that match
701 // the query conditions.
702 // We should use the appropriate Titan/Gremlin query to filter-out
703 // the flows as appropriate.
704 //
705 ArrayList<FlowPath> allFlows = getAllFlows();
706
707 if (allFlows == null) {
708 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
709 return null;
710 }
711
712 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
713 for (FlowPath flow : allFlows) {
714 //
715 // TODO: String-based comparison is sub-optimal.
716 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800717 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800718 //
719 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
720 continue;
721 }
722 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
723 continue;
724 }
725 flowPaths.add(flow);
726 }
727
728 if (flowPaths.isEmpty()) {
729 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
730 flowPaths = null;
731 } else {
732 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
733 }
734
735 return flowPaths;
736 }
737
738 /**
739 * Get all installed flows by all installers.
740 *
741 * @return the Flow Paths if found, otherwise null.
742 */
743 @Override
744 public ArrayList<FlowPath> getAllFlows() {
745 Iterable<IFlowPath> flowPathsObj = null;
746
747 try {
748 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
749 log.debug("Get all FlowPaths: found FlowPaths");
750 } else {
751 log.debug("Get all FlowPaths: no FlowPaths found");
752 }
753 } catch (Exception e) {
754 // TODO: handle exceptions
755 conn.endTx(Transaction.ROLLBACK);
756 log.error(":getAllFlowPaths failed");
757 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700758 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
759 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800760 return null; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700761 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800762
763 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
764 for (IFlowPath flowObj : flowPathsObj) {
765 //
766 // Extract the Flow state
767 //
768 FlowPath flowPath = extractFlowPath(flowObj);
769 flowPaths.add(flowPath);
770 }
771
772 conn.endTx(Transaction.COMMIT);
773
774 return flowPaths;
775 }
776
777 /**
778 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
779 *
780 * @param flowObj the object to extract the Flow Path State from.
781 * @return the extracted Flow Path State.
782 */
783 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800784 FlowPath flowPath = new FlowPath();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800785
786 //
787 // Extract the Flow state
788 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800789 flowPath.setFlowId(new FlowId(flowObj.getFlowId()));
790 flowPath.setInstallerId(new CallerId(flowObj.getInstallerId()));
791 flowPath.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
792 flowPath.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
793 flowPath.dataPath().dstPort().setDpid(new Dpid(flowObj.getDstSwitch()));
794 flowPath.dataPath().dstPort().setPort(new Port(flowObj.getDstPort()));
795
796 //
797 // Extract all Flow Entries
798 //
799 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
800 for (IFlowEntry flowEntryObj : flowEntries) {
801 FlowEntry flowEntry = new FlowEntry();
802 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryObj.getFlowEntryId()));
803 flowEntry.setDpid(new Dpid(flowEntryObj.getSwitchDpid()));
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700804
805 //
806 // Extract the match conditions
807 //
808 FlowEntryMatch match = new FlowEntryMatch();
809 Short matchInPort = flowEntryObj.getMatchInPort();
810 if (matchInPort != null)
811 match.enableInPort(new Port(matchInPort));
812 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
813 if (matchEthernetFrameType != null)
814 match.enableEthernetFrameType(matchEthernetFrameType);
815 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
816 if (matchSrcIPv4Net != null)
817 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
818 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
819 if (matchDstIPv4Net != null)
820 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
821 String matchSrcMac = flowEntryObj.getMatchSrcMac();
822 if (matchSrcMac != null)
823 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
824 String matchDstMac = flowEntryObj.getMatchDstMac();
825 if (matchDstMac != null)
826 match.enableDstMac(MACAddress.valueOf(matchDstMac));
827 flowEntry.setFlowEntryMatch(match);
828
829 //
830 // Extract the actions
831 //
832 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
833 Short actionOutputPort = flowEntryObj.getActionOutput();
834 if (actionOutputPort != null) {
835 FlowEntryAction action = new FlowEntryAction();
836 action.setActionOutput(new Port(actionOutputPort));
837 actions.add(action);
838 }
839 flowEntry.setFlowEntryActions(actions);
840
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800841 String userState = flowEntryObj.getUserState();
842 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
843 String switchState = flowEntryObj.getSwitchState();
844 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
845 //
Pavlin Radoslavovede97582013-03-08 18:57:28 -0800846 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800847 // and FlowEntryErrorState.
848 //
849 flowPath.dataPath().flowEntries().add(flowEntry);
850 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800851
852 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800853 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800854}