blob: d55979eeb18e9c779ab7d54e43175a693d7d2231 [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 Radoslavov79a67c12013-03-15 21:05:53 -0700104 String userState = "User State: " + flowEntryObj.getUserState();
105 String switchState = "Switch State: " + 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(),
109 userState + " " + switchState);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800110
111 if (! switchState.equals("FE_SWITCH_NOT_UPDATED")) {
112 // Ignore the entry: nothing to do
113 continue;
114 }
115
116 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
117 IOFSwitch mySwitch = mySwitches.get(dpid.value());
118 if (mySwitch == null) {
Pavlin Radoslavov79a67c12013-03-15 21:05:53 -0700119 log.debug("Flow Entry ignored: not my switch (FlowEntryId = {} DPID = {})", flowEntryId.toString(), dpid.toString());
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800120 continue;
121 }
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700122 myFlowEntries.put(flowEntryId.value(), flowEntryObj);
123 }
124
125 //
126 // Process my Flow Entries
127 //
128 for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
129 IFlowEntry flowEntryObj = entry.getValue();
130
131 //
132 // TODO: Eliminate the re-fetching of flowEntryId,
133 // userState, switchState, and dpid from the flowEntryObj.
134 //
135 FlowEntryId flowEntryId =
136 new FlowEntryId(flowEntryObj.getFlowEntryId());
137 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
138 String userState = flowEntryObj.getUserState();
139 String switchState = flowEntryObj.getSwitchState();
140 IOFSwitch mySwitch = mySwitches.get(dpid.value());
141 if (mySwitch == null) {
142 log.debug("Flow Entry ignored: not my switch");
143 continue;
144 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800145
146 //
147 // Create the Open Flow Flow Modification Entry to push
148 //
149 OFFlowMod fm =
150 (OFFlowMod) floodlightProvider.getOFMessageFactory()
151 .getMessage(OFType.FLOW_MOD);
152 long cookie = flowEntryId.value();
153
154 short flowModCommand = OFFlowMod.OFPFC_ADD;
155 if (userState.equals("FE_USER_ADD")) {
156 flowModCommand = OFFlowMod.OFPFC_ADD;
157 } else if (userState.equals("FE_USER_MODIFY")) {
158 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
159 } else if (userState.equals("FE_USER_DELETE")) {
160 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
161 } else {
162 // Unknown user state. Ignore the entry
163 continue;
164 }
165
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700166 //
167 // Fetch the match conditions
168 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800169 OFMatch match = new OFMatch();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700170 match.setWildcards(OFMatch.OFPFW_ALL);
171 Short matchInPort = flowEntryObj.getMatchInPort();
172 if (matchInPort != null) {
173 match.setInputPort(matchInPort);
174 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
175 }
176 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
177 if (matchEthernetFrameType != null) {
178 match.setDataLayerType(matchEthernetFrameType);
179 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
180 }
181 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
182 if (matchSrcIPv4Net != null) {
183 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
184 }
185 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
186 if (matchDstIPv4Net != null) {
187 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
188 }
189 String matchSrcMac = flowEntryObj.getMatchSrcMac();
190 if (matchSrcMac != null) {
191 match.setDataLayerSource(matchSrcMac);
192 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
193 }
194 String matchDstMac = flowEntryObj.getMatchDstMac();
195 if (matchDstMac != null) {
196 match.setDataLayerDestination(matchDstMac);
197 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
198 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700199
200 //
201 // Fetch the actions
202 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800203 List<OFAction> actions = new ArrayList<OFAction>();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700204 Short actionOutputPort = flowEntryObj.getActionOutput();
205 if (actionOutputPort != null) {
206 OFActionOutput action = new OFActionOutput();
207 // XXX: The max length is hard-coded for now
208 action.setMaxLength((short)0xffff);
209 action.setPort(actionOutputPort);
210 actions.add(action);
211 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800212
213 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
214 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700215 .setPriority(PRIORITY_DEFAULT)
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800216 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
217 .setCookie(cookie)
218 .setCommand(flowModCommand)
219 .setMatch(match)
220 .setActions(actions)
221 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700222 fm.setOutPort(OFPort.OFPP_NONE.getValue());
223 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
224 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
225 if (actionOutputPort != null)
226 fm.setOutPort(actionOutputPort);
227 }
228
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800229 //
230 // TODO: Set the following flag
231 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
232 // See method ForwardingBase::pushRoute()
233 //
234 try {
235 messageDamper.write(mySwitch, fm, null);
236 mySwitch.flush();
237 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
238 if (userState.equals("FE_USER_DELETE")) {
239 // Delete the entry
240 IFlowPath flowObj = null;
241 flowObj = conn.utils().getFlowPathByFlowEntry(conn,
242 flowEntryObj);
243 if (flowObj != null)
244 log.debug("Found FlowPath to be deleted");
245 else
246 log.debug("Did not find FlowPath to be deleted");
247 flowObj.removeFlowEntry(flowEntryObj);
248 conn.utils().removeFlowEntry(conn, flowEntryObj);
249
250 // Test whether the last flow entry
251 Iterable<IFlowEntry> tmpflowEntries =
252 flowObj.getFlowEntries();
253 boolean found = false;
254 for (IFlowEntry tmpflowEntryObj : tmpflowEntries) {
255 found = true;
256 break;
257 }
258 if (! found) {
259 // Remove the Flow Path as well
260 conn.utils().removeFlowPath(conn, flowObj);
261 }
262 }
263 } catch (IOException e) {
264 log.error("Failure writing flow mod from network map", e);
265 }
266 }
267 conn.endTx(Transaction.COMMIT);
268 }
269 };
270 final ScheduledFuture<?> readerHandle =
271 scheduler.scheduleAtFixedRate(reader, 3, 3, TimeUnit.SECONDS);
272
273 @Override
274 public void init(String conf) {
275 conn = GraphDBConnection.getInstance(conf);
276 }
277
278 public void finalize() {
279 close();
280 }
281
282 @Override
283 public void close() {
284 conn.close();
285 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800286
287 @Override
288 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
289 Collection<Class<? extends IFloodlightService>> l =
290 new ArrayList<Class<? extends IFloodlightService>>();
291 l.add(IFlowService.class);
292 return l;
293 }
294
295 @Override
296 public Map<Class<? extends IFloodlightService>, IFloodlightService>
297 getServiceImpls() {
298 Map<Class<? extends IFloodlightService>,
299 IFloodlightService> m =
300 new HashMap<Class<? extends IFloodlightService>,
301 IFloodlightService>();
302 m.put(IFlowService.class, this);
303 return m;
304 }
305
306 @Override
307 public Collection<Class<? extends IFloodlightService>>
308 getModuleDependencies() {
309 Collection<Class<? extends IFloodlightService>> l =
310 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800311 l.add(IFloodlightProviderService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800312 l.add(IRestApiService.class);
313 return l;
314 }
315
316 @Override
317 public void init(FloodlightModuleContext context)
318 throws FloodlightModuleException {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800319 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800320 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800321 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
322 EnumSet.of(OFType.FLOW_MOD),
323 OFMESSAGE_DAMPER_TIMEOUT);
324 // TODO: An ugly hack!
325 String conf = "/tmp/cassandra.titan";
326 this.init(conf);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800327 }
328
329 @Override
330 public void startUp(FloodlightModuleContext context) {
331 restApi.addRestletRoutable(new FlowWebRoutable());
332 }
333
334 /**
335 * Add a flow.
336 *
337 * Internally, ONOS will automatically register the installer for
338 * receiving Flow Path Notifications for that path.
339 *
340 * @param flowPath the Flow Path to install.
341 * @param flowId the return-by-reference Flow ID as assigned internally.
342 * @return true on success, otherwise false.
343 */
344 @Override
345 public boolean addFlow(FlowPath flowPath, FlowId flowId) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800346
347 //
348 // Assign the FlowEntry IDs
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700349 // Right now every new flow entry gets a new flow entry ID
350 // TODO: This needs to be redesigned!
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800351 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800352 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700353 long id = nextFlowEntryId++;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800354 flowEntry.setFlowEntryId(new FlowEntryId(id));
355 }
356
357 IFlowPath flowObj = null;
358 try {
359 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
360 != null) {
361 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
362 flowPath.flowId().toString());
363 } else {
364 flowObj = conn.utils().newFlowPath(conn);
365 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
366 flowPath.flowId().toString());
367 }
368 } catch (Exception e) {
369 // TODO: handle exceptions
370 conn.endTx(Transaction.ROLLBACK);
371 log.error(":addFlow FlowId:{} failed",
372 flowPath.flowId().toString());
373 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700374 if (flowObj == null) {
375 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800376 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700377 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800378
379 //
380 // Set the Flow key:
381 // - flowId
382 //
383 flowObj.setFlowId(flowPath.flowId().toString());
384 flowObj.setType("flow");
385
386 //
387 // Set the Flow attributes:
388 // - flowPath.installerId()
389 // - flowPath.dataPath().srcPort()
390 // - flowPath.dataPath().dstPort()
391 //
392 flowObj.setInstallerId(flowPath.installerId().toString());
393 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
394 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
395 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
396 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
397
398 // Flow edges:
399 // HeadFE
400
401
402 //
403 // Flow Entries:
404 // flowPath.dataPath().flowEntries()
405 //
406 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
407 IFlowEntry flowEntryObj = null;
408 boolean found = false;
409 try {
410 if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
411 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
412 flowEntry.flowEntryId().toString());
413 found = true;
414 } else {
415 flowEntryObj = conn.utils().newFlowEntry(conn);
416 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
417 flowEntry.flowEntryId().toString());
418 }
419 } catch (Exception e) {
420 // TODO: handle exceptions
421 conn.endTx(Transaction.ROLLBACK);
422 log.error(":addFlow FlowEntryId:{} failed",
423 flowEntry.flowEntryId().toString());
424 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700425 if (flowEntryObj == null) {
426 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800427 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700428 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800429
430 //
431 // Set the Flow Entry key:
432 // - flowEntry.flowEntryId()
433 //
434 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
435 flowEntryObj.setType("flow_entry");
436
437 //
438 // Set the Flow Entry attributes:
439 // - flowEntry.flowEntryMatch()
440 // - flowEntry.flowEntryActions()
441 // - flowEntry.dpid()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800442 // - flowEntry.flowEntryUserState()
443 // - flowEntry.flowEntrySwitchState()
444 // - flowEntry.flowEntryErrorState()
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700445 // - flowEntry.matchInPort()
446 // - flowEntry.matchEthernetFrameType()
447 // - flowEntry.matchSrcIPv4Net()
448 // - flowEntry.matchDstIPv4Net()
449 // - flowEntry.matchSrcMac()
450 // - flowEntry.matchDstMac()
451 // - flowEntry.actionOutput()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800452 //
453 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700454 if (flowEntry.flowEntryMatch().matchInPort())
455 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
456 if (flowEntry.flowEntryMatch().matchEthernetFrameType())
457 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
458 if (flowEntry.flowEntryMatch().matchSrcIPv4Net())
459 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
460 if (flowEntry.flowEntryMatch().matchDstIPv4Net())
461 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
462 if (flowEntry.flowEntryMatch().matchSrcMac())
463 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
464 if (flowEntry.flowEntryMatch().matchDstMac())
465 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
466
467 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
468 if (fa.actionOutput() != null)
469 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
470 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800471 // TODO: Hacks with hard-coded state names!
472 if (found)
473 flowEntryObj.setUserState("FE_USER_MODIFY");
474 else
475 flowEntryObj.setUserState("FE_USER_ADD");
476 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
477 //
Pavlin Radoslavovede97582013-03-08 18:57:28 -0800478 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800479 // and FlowEntryErrorState.
480 //
481
482 // Flow Entries edges:
483 // Flow
484 // NextFE
485 // InPort
486 // OutPort
487 // Switch
488 if (! found)
489 flowObj.addFlowEntry(flowEntryObj);
490 }
491 conn.endTx(Transaction.COMMIT);
492
493 //
494 // TODO: We need a proper Flow ID allocation mechanism.
495 //
496 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700497
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800498 return true;
499 }
500
501 /**
502 * Delete a previously added flow.
503 *
504 * @param flowId the Flow ID of the flow to delete.
505 * @return true on success, otherwise false.
506 */
507 @Override
508 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800509 IFlowPath flowObj = null;
510 //
511 // We just mark the entries for deletion,
512 // and let the switches remove each individual entry after
513 // it has been removed from the switches.
514 //
515 try {
516 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
517 != null) {
518 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
519 flowId.toString());
520 } else {
521 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
522 flowId.toString());
523 }
524 } catch (Exception e) {
525 // TODO: handle exceptions
526 conn.endTx(Transaction.ROLLBACK);
527 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
528 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700529 if (flowObj == null) {
530 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800531 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700532 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800533
534 //
535 // Find and mark for deletion all Flow Entries
536 //
537 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
538 boolean empty = true; // TODO: an ugly hack
539 for (IFlowEntry flowEntryObj : flowEntries) {
540 empty = false;
541 // flowObj.removeFlowEntry(flowEntryObj);
542 // conn.utils().removeFlowEntry(conn, flowEntryObj);
543 flowEntryObj.setUserState("FE_USER_DELETE");
544 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
545 }
546 // Remove from the database empty flows
547 if (empty)
548 conn.utils().removeFlowPath(conn, flowObj);
549 conn.endTx(Transaction.COMMIT);
550
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800551 return true;
552 }
553
554 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700555 * Clear the state for a previously added flow.
556 *
557 * @param flowId the Flow ID of the flow to clear.
558 * @return true on success, otherwise false.
559 */
560 @Override
561 public boolean clearFlow(FlowId flowId) {
562 IFlowPath flowObj = null;
563 try {
564 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
565 != null) {
566 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
567 flowId.toString());
568 } else {
569 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
570 flowId.toString());
571 }
572 } catch (Exception e) {
573 // TODO: handle exceptions
574 conn.endTx(Transaction.ROLLBACK);
575 log.error(":clearFlow FlowId:{} failed", flowId.toString());
576 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700577 if (flowObj == null) {
578 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700579 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700580 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700581
582 //
583 // Remove all Flow Entries
584 //
585 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
586 for (IFlowEntry flowEntryObj : flowEntries) {
587 flowObj.removeFlowEntry(flowEntryObj);
588 conn.utils().removeFlowEntry(conn, flowEntryObj);
589 }
590 // Remove the Flow itself
591 conn.utils().removeFlowPath(conn, flowObj);
592 conn.endTx(Transaction.COMMIT);
593
594 return true;
595 }
596
597 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800598 * Get a previously added flow.
599 *
600 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800601 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800602 */
603 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800604 public FlowPath getFlow(FlowId flowId) {
605 IFlowPath flowObj = null;
606 try {
607 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
608 != null) {
609 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
610 flowId.toString());
611 } else {
612 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
613 flowId.toString());
614 }
615 } catch (Exception e) {
616 // TODO: handle exceptions
617 conn.endTx(Transaction.ROLLBACK);
618 log.error(":getFlow FlowId:{} failed", flowId.toString());
619 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700620 if (flowObj == null) {
621 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800622 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700623 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800624
625 //
626 // Extract the Flow state
627 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800628 FlowPath flowPath = extractFlowPath(flowObj);
629 conn.endTx(Transaction.COMMIT);
630
631 return flowPath;
632 }
633
634 /**
635 * Get all previously added flows by a specific installer for a given
636 * data path endpoints.
637 *
638 * @param installerId the Caller ID of the installer of the flow to get.
639 * @param dataPathEndpoints the data path endpoints of the flow to get.
640 * @return the Flow Paths if found, otherwise null.
641 */
642 @Override
643 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
644 DataPathEndpoints dataPathEndpoints) {
645 //
646 // TODO: The implementation below is not optimal:
647 // We fetch all flows, and then return only the subset that match
648 // the query conditions.
649 // We should use the appropriate Titan/Gremlin query to filter-out
650 // the flows as appropriate.
651 //
652 ArrayList<FlowPath> allFlows = getAllFlows();
653
654 if (allFlows == null) {
655 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
656 return null;
657 }
658
659 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
660 for (FlowPath flow : allFlows) {
661 //
662 // TODO: String-based comparison is sub-optimal.
663 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800664 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800665 //
666 if (! flow.installerId().toString().equals(installerId.toString()))
667 continue;
668 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
669 continue;
670 }
671 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
672 continue;
673 }
674 flowPaths.add(flow);
675 }
676
677 if (flowPaths.isEmpty()) {
678 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
679 flowPaths = null;
680 } else {
681 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
682 }
683
684 return flowPaths;
685 }
686
687 /**
688 * Get all installed flows by all installers for given data path endpoints.
689 *
690 * @param dataPathEndpoints the data path endpoints of the flows to get.
691 * @return the Flow Paths if found, otherwise null.
692 */
693 @Override
694 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
695 //
696 // TODO: The implementation below is not optimal:
697 // We fetch all flows, and then return only the subset that match
698 // the query conditions.
699 // We should use the appropriate Titan/Gremlin query to filter-out
700 // the flows as appropriate.
701 //
702 ArrayList<FlowPath> allFlows = getAllFlows();
703
704 if (allFlows == null) {
705 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
706 return null;
707 }
708
709 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
710 for (FlowPath flow : allFlows) {
711 //
712 // TODO: String-based comparison is sub-optimal.
713 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800714 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800715 //
716 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
717 continue;
718 }
719 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
720 continue;
721 }
722 flowPaths.add(flow);
723 }
724
725 if (flowPaths.isEmpty()) {
726 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
727 flowPaths = null;
728 } else {
729 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
730 }
731
732 return flowPaths;
733 }
734
735 /**
736 * Get all installed flows by all installers.
737 *
738 * @return the Flow Paths if found, otherwise null.
739 */
740 @Override
741 public ArrayList<FlowPath> getAllFlows() {
742 Iterable<IFlowPath> flowPathsObj = null;
743
744 try {
745 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
746 log.debug("Get all FlowPaths: found FlowPaths");
747 } else {
748 log.debug("Get all FlowPaths: no FlowPaths found");
749 }
750 } catch (Exception e) {
751 // TODO: handle exceptions
752 conn.endTx(Transaction.ROLLBACK);
753 log.error(":getAllFlowPaths failed");
754 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700755 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
756 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800757 return null; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700758 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800759
760 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
761 for (IFlowPath flowObj : flowPathsObj) {
762 //
763 // Extract the Flow state
764 //
765 FlowPath flowPath = extractFlowPath(flowObj);
766 flowPaths.add(flowPath);
767 }
768
769 conn.endTx(Transaction.COMMIT);
770
771 return flowPaths;
772 }
773
774 /**
775 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
776 *
777 * @param flowObj the object to extract the Flow Path State from.
778 * @return the extracted Flow Path State.
779 */
780 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800781 FlowPath flowPath = new FlowPath();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800782
783 //
784 // Extract the Flow state
785 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800786 flowPath.setFlowId(new FlowId(flowObj.getFlowId()));
787 flowPath.setInstallerId(new CallerId(flowObj.getInstallerId()));
788 flowPath.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
789 flowPath.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
790 flowPath.dataPath().dstPort().setDpid(new Dpid(flowObj.getDstSwitch()));
791 flowPath.dataPath().dstPort().setPort(new Port(flowObj.getDstPort()));
792
793 //
794 // Extract all Flow Entries
795 //
796 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
797 for (IFlowEntry flowEntryObj : flowEntries) {
798 FlowEntry flowEntry = new FlowEntry();
799 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryObj.getFlowEntryId()));
800 flowEntry.setDpid(new Dpid(flowEntryObj.getSwitchDpid()));
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700801
802 //
803 // Extract the match conditions
804 //
805 FlowEntryMatch match = new FlowEntryMatch();
806 Short matchInPort = flowEntryObj.getMatchInPort();
807 if (matchInPort != null)
808 match.enableInPort(new Port(matchInPort));
809 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
810 if (matchEthernetFrameType != null)
811 match.enableEthernetFrameType(matchEthernetFrameType);
812 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
813 if (matchSrcIPv4Net != null)
814 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
815 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
816 if (matchDstIPv4Net != null)
817 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
818 String matchSrcMac = flowEntryObj.getMatchSrcMac();
819 if (matchSrcMac != null)
820 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
821 String matchDstMac = flowEntryObj.getMatchDstMac();
822 if (matchDstMac != null)
823 match.enableDstMac(MACAddress.valueOf(matchDstMac));
824 flowEntry.setFlowEntryMatch(match);
825
826 //
827 // Extract the actions
828 //
829 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
830 Short actionOutputPort = flowEntryObj.getActionOutput();
831 if (actionOutputPort != null) {
832 FlowEntryAction action = new FlowEntryAction();
833 action.setActionOutput(new Port(actionOutputPort));
834 actions.add(action);
835 }
836 flowEntry.setFlowEntryActions(actions);
837
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800838 String userState = flowEntryObj.getUserState();
839 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
840 String switchState = flowEntryObj.getSwitchState();
841 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
842 //
Pavlin Radoslavovede97582013-03-08 18:57:28 -0800843 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800844 // and FlowEntryErrorState.
845 //
846 flowPath.dataPath().flowEntries().add(flowEntry);
847 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800848
849 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800850 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800851}