blob: 7676cb0429d7948a4f896c5ca4fac82e175fedc7 [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 Radoslavov0b22d0e2013-04-02 01:12:46 +00006import java.util.Collections;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08007import java.util.EnumSet;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08008import java.util.HashMap;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07009import java.util.HashSet;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +000010import java.util.LinkedList;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080011import java.util.List;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080012import java.util.Map;
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +000013import java.util.Random;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070014import java.util.TreeMap;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070015import java.util.concurrent.BlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080016import java.util.concurrent.Executors;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070017import java.util.concurrent.LinkedBlockingQueue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080018import java.util.concurrent.ScheduledExecutorService;
19import java.util.concurrent.ScheduledFuture;
Pavlin Radoslavov4da61282013-03-20 20:31:36 -070020import java.util.concurrent.ThreadPoolExecutor;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080021import java.util.concurrent.TimeUnit;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080022
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080023import net.floodlightcontroller.core.IFloodlightProviderService;
24import net.floodlightcontroller.core.INetMapStorage;
25import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
26import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -070027import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
Pankaj Berded0079742013-03-27 17:53:25 -070028import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070029import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080030import net.floodlightcontroller.core.IOFSwitch;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080031import net.floodlightcontroller.core.module.FloodlightModuleContext;
32import net.floodlightcontroller.core.module.FloodlightModuleException;
33import net.floodlightcontroller.core.module.IFloodlightModule;
34import net.floodlightcontroller.core.module.IFloodlightService;
35import net.floodlightcontroller.flowcache.IFlowService;
36import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
37import net.floodlightcontroller.restserver.IRestApiService;
38import net.floodlightcontroller.util.CallerId;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080039import net.floodlightcontroller.util.DataPath;
40import net.floodlightcontroller.util.Dpid;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080041import net.floodlightcontroller.util.DataPathEndpoints;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080042import net.floodlightcontroller.util.FlowEntry;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070043import net.floodlightcontroller.util.FlowEntryAction;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080044import net.floodlightcontroller.util.FlowEntryId;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070045import net.floodlightcontroller.util.FlowEntryMatch;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080046import net.floodlightcontroller.util.FlowEntrySwitchState;
47import net.floodlightcontroller.util.FlowEntryUserState;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -080048import net.floodlightcontroller.util.FlowId;
49import net.floodlightcontroller.util.FlowPath;
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -070050import net.floodlightcontroller.util.IPv4Net;
51import net.floodlightcontroller.util.MACAddress;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -080052import net.floodlightcontroller.util.OFMessageDamper;
53import net.floodlightcontroller.util.Port;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070054import net.floodlightcontroller.util.SwitchPort;
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 Radoslavov5adf1522013-04-04 17:43:41 -070069public class FlowManager implements IFloodlightModule, IFlowService, 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 Radoslavov0b22d0e2013-04-02 01:12:46 +000090 // Flow Entry ID generation state
91 private static Random randomGenerator = new Random();
92 private static int nextFlowEntryIdPrefix = 0;
93 private static int nextFlowEntryIdSuffix = 0;
94 private static long nextFlowEntryId = 0;
95
Pavlin Radoslavov571cff92013-03-20 02:01:32 -070096 private static long measurementFlowId = 100000;
97 private static String measurementFlowIdStr = "0x186a0"; // 100000
98 private long modifiedMeasurementFlowTime = 0;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -070099
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800100 /** The logger. */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800101 private static Logger log = LoggerFactory.getLogger(FlowManager.class);
102
103 // The periodic task(s)
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700104 private final ScheduledExecutorService mapReaderScheduler =
105 Executors.newScheduledThreadPool(1);
106
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700107 final Runnable mapReader = new Runnable() {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800108 public void run() {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700109 long startTime = System.nanoTime();
110 int counterAllFlowEntries = 0;
111 int counterMyNotUpdatedFlowEntries = 0;
112 int counterAllFlowPaths = 0;
113 int counterMyFlowPaths = 0;
114
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800115 if (floodlightProvider == null) {
116 log.debug("FloodlightProvider service not found!");
117 return;
118 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000119 Map<Long, IOFSwitch> mySwitches =
120 floodlightProvider.getSwitches();
121 Map<Long, IFlowEntry> myFlowEntries =
122 new TreeMap<Long, IFlowEntry>();
123 LinkedList<IFlowEntry> deleteFlowEntries =
124 new LinkedList<IFlowEntry>();
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700125
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700126
127 //
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700128 // Fetch all Flow Entries and select only my Flow Entries
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700129 // that need to be updated into the switches.
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700130 //
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000131 Iterable<IFlowEntry> allFlowEntries =
132 conn.utils().getAllFlowEntries(conn);
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700133 for (IFlowEntry flowEntryObj : allFlowEntries) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700134 counterAllFlowEntries++;
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000135 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700136 String userState = flowEntryObj.getUserState();
137 String switchState = flowEntryObj.getSwitchState();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000138 String dpidStr = flowEntryObj.getSwitchDpid();
139 if ((flowEntryIdStr == null) ||
140 (userState == null) ||
141 (switchState == null) ||
142 (dpidStr == null)) {
143 log.debug("IGNORING Flow Entry entry with null fields");
144 continue;
145 }
146 FlowEntryId flowEntryId = new FlowEntryId(flowEntryIdStr);
147 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800148
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000149 if (! switchState.equals("FE_SWITCH_NOT_UPDATED"))
150 continue; // Ignore the entry: nothing to do
151
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800152 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000153 if (mySwitch == null)
154 continue; // Ignore the entry: not my switch
155
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700156 myFlowEntries.put(flowEntryId.value(), flowEntryObj);
157 }
158
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700159 log.debug("MEASUREMENT: Found {} My Flow Entries NOT_UPDATED",
160 myFlowEntries.size());
161
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700162 //
163 // Process my Flow Entries
164 //
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700165 boolean processed_measurement_flow = false;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700166 for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700167 counterMyNotUpdatedFlowEntries++;
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700168 IFlowEntry flowEntryObj = entry.getValue();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700169 IFlowPath flowObj =
170 conn.utils().getFlowPathByFlowEntry(conn,
171 flowEntryObj);
172 if (flowObj == null)
173 continue; // Should NOT happen
174
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700175 // Code for measurement purpose
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700176 // TODO: Commented-out for now
177 /*
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700178 {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700179 if (flowObj.getFlowId().equals(measurementFlowIdStr)) {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700180 processed_measurement_flow = true;
181 }
182 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700183 */
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700184
185 //
186 // TODO: Eliminate the re-fetching of flowEntryId,
187 // userState, switchState, and dpid from the flowEntryObj.
188 //
189 FlowEntryId flowEntryId =
190 new FlowEntryId(flowEntryObj.getFlowEntryId());
191 Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
192 String userState = flowEntryObj.getUserState();
193 String switchState = flowEntryObj.getSwitchState();
194 IOFSwitch mySwitch = mySwitches.get(dpid.value());
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000195 if (mySwitch == null)
196 continue; // Shouldn't happen
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800197
198 //
199 // Create the Open Flow Flow Modification Entry to push
200 //
201 OFFlowMod fm =
202 (OFFlowMod) floodlightProvider.getOFMessageFactory()
203 .getMessage(OFType.FLOW_MOD);
204 long cookie = flowEntryId.value();
205
206 short flowModCommand = OFFlowMod.OFPFC_ADD;
207 if (userState.equals("FE_USER_ADD")) {
208 flowModCommand = OFFlowMod.OFPFC_ADD;
209 } else if (userState.equals("FE_USER_MODIFY")) {
210 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
211 } else if (userState.equals("FE_USER_DELETE")) {
212 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
213 } else {
214 // Unknown user state. Ignore the entry
Pavlin Radoslavov2f9d6332013-03-18 23:05:48 -0700215 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
216 flowEntryId.toString(), userState);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800217 continue;
218 }
219
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700220 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700221 // Fetch the match conditions.
222 //
223 // NOTE: The Flow matching conditions common for all
224 // Flow Entries are used ONLY if a Flow Entry does NOT
225 // have the corresponding matching condition set.
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700226 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800227 OFMatch match = new OFMatch();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700228 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700229 //
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700230 Short matchInPort = flowEntryObj.getMatchInPort();
231 if (matchInPort != null) {
232 match.setInputPort(matchInPort);
233 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
234 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700235 //
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700236 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700237 if (matchEthernetFrameType == null)
238 matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700239 if (matchEthernetFrameType != null) {
240 match.setDataLayerType(matchEthernetFrameType);
241 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
242 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700243 //
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700244 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700245 if (matchSrcIPv4Net == null)
246 matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700247 if (matchSrcIPv4Net != null) {
248 match.setFromCIDR(matchSrcIPv4Net, OFMatch.STR_NW_SRC);
249 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700250 //
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700251 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700252 if (matchDstIPv4Net == null)
253 matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700254 if (matchDstIPv4Net != null) {
255 match.setFromCIDR(matchDstIPv4Net, OFMatch.STR_NW_DST);
256 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700257 //
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700258 String matchSrcMac = flowEntryObj.getMatchSrcMac();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700259 if (matchSrcMac == null)
260 matchSrcMac = flowObj.getMatchSrcMac();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700261 if (matchSrcMac != null) {
262 match.setDataLayerSource(matchSrcMac);
263 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
264 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700265 //
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700266 String matchDstMac = flowEntryObj.getMatchDstMac();
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700267 if (matchDstMac == null)
268 matchDstMac = flowObj.getMatchDstMac();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700269 if (matchDstMac != null) {
270 match.setDataLayerDestination(matchDstMac);
271 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
272 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700273
274 //
275 // Fetch the actions
276 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800277 List<OFAction> actions = new ArrayList<OFAction>();
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700278 Short actionOutputPort = flowEntryObj.getActionOutput();
279 if (actionOutputPort != null) {
280 OFActionOutput action = new OFActionOutput();
281 // XXX: The max length is hard-coded for now
282 action.setMaxLength((short)0xffff);
283 action.setPort(actionOutputPort);
284 actions.add(action);
285 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800286
287 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
288 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700289 .setPriority(PRIORITY_DEFAULT)
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800290 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
291 .setCookie(cookie)
292 .setCommand(flowModCommand)
293 .setMatch(match)
294 .setActions(actions)
295 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
Pavlin Radoslavov78c4e492013-03-12 17:17:48 -0700296 fm.setOutPort(OFPort.OFPP_NONE.getValue());
297 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
298 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
299 if (actionOutputPort != null)
300 fm.setOutPort(actionOutputPort);
301 }
302
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800303 //
304 // TODO: Set the following flag
305 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
306 // See method ForwardingBase::pushRoute()
307 //
308 try {
309 messageDamper.write(mySwitch, fm, null);
310 mySwitch.flush();
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000311 //
312 // TODO: We should use the OpenFlow Barrier mechanism
313 // to check for errors, and update the SwitchState
314 // for a flow entry after the Barrier message is
315 // is received.
316 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800317 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
318 if (userState.equals("FE_USER_DELETE")) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000319 // An entry that needs to be deleted.
320 deleteFlowEntries.add(flowEntryObj);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800321 }
322 } catch (IOException e) {
323 log.error("Failure writing flow mod from network map", e);
324 }
325 }
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000326
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700327 log.debug("MEASUREMENT: Found {} Flow Entries to delete",
328 deleteFlowEntries.size());
329
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000330 //
331 // Delete all entries marked for deletion
332 //
333 // TODO: We should use the OpenFlow Barrier mechanism
334 // to check for errors, and delete the Flow Entries after the
335 // Barrier message is received.
336 //
337 while (! deleteFlowEntries.isEmpty()) {
338 IFlowEntry flowEntryObj = deleteFlowEntries.poll();
339 IFlowPath flowObj =
340 conn.utils().getFlowPathByFlowEntry(conn, flowEntryObj);
341 if (flowObj == null) {
342 log.debug("Did not find FlowPath to be deleted");
343 continue;
344 }
345 flowObj.removeFlowEntry(flowEntryObj);
346 conn.utils().removeFlowEntry(conn, flowEntryObj);
347
348 // Test whether the last flow entry
349 Iterable<IFlowEntry> tmpflowEntries =
350 flowObj.getFlowEntries();
351 boolean found = false;
352 for (IFlowEntry tmpflowEntryObj : tmpflowEntries) {
353 found = true;
354 break;
355 }
356 if (! found) {
357 // Remove the Flow Path as well
358 conn.utils().removeFlowPath(conn, flowObj);
359 }
360 }
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700361
362
363 //
364 // Fetch and recompute the Shortest Path for those
365 // Flow Paths this controller is responsible for.
366 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700367 topoRouteService.prepareShortestPathTopo();
368 Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
369 HashSet<IFlowPath> flowObjSet = new HashSet<IFlowPath>();
370 for (IFlowPath flowPathObj : allFlowPaths) {
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700371 counterAllFlowPaths++;
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700372 if (flowPathObj == null)
373 continue;
374 String dataPathSummaryStr = flowPathObj.getDataPathSummary();
375 if (dataPathSummaryStr == null)
376 continue; // Could be invalid entry?
377 if (dataPathSummaryStr.isEmpty())
378 continue; // No need to maintain this flow
379
380 // Fetch the fields needed to recompute the shortest path
381 String flowIdStr = flowPathObj.getFlowId();
382 String srcDpidStr = flowPathObj.getSrcSwitch();
383 Short srcPortShort = flowPathObj.getSrcPort();
384 String dstDpidStr = flowPathObj.getDstSwitch();
385 Short dstPortShort = flowPathObj.getDstPort();
386 if ((flowIdStr == null) ||
387 (srcDpidStr == null) ||
388 (srcPortShort == null) ||
389 (dstDpidStr == null) ||
390 (dstPortShort == null)) {
391 log.debug("IGNORING Flow Path entry with null fields");
392 continue;
393 }
394
395 FlowId flowId = new FlowId(flowIdStr);
396 Dpid srcDpid = new Dpid(srcDpidStr);
397 Port srcPort = new Port(srcPortShort);
398 Dpid dstDpid = new Dpid(dstDpidStr);
399 Port dstPort = new Port(dstPortShort);
400 SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
401 SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
Pavlin Radoslavov2659a0b2013-04-03 20:30:40 -0700402
403 //
404 // Use the source DPID as a heuristic to decide
405 // which controller is responsible for maintaining the
406 // shortest path.
407 // NOTE: This heuristic is error-prone: if the switch
408 // goes away and no controller is responsible for that
409 // switch, then the original Flow Path is not cleaned-up
410 //
411 IOFSwitch mySwitch = mySwitches.get(srcDpid.value());
412 if (mySwitch == null)
413 continue; // Ignore: not my responsibility
414
415 counterMyFlowPaths++;
416
Pavlin Radoslavov832aa652013-03-29 16:21:59 -0700417 //
418 // NOTE: Using here the regular getShortestPath() method
419 // won't work here, because that method calls internally
420 // "conn.endTx(Transaction.COMMIT)", and that will
421 // invalidate all handlers to the Titan database.
422 // If we want to experiment with calling here
423 // getShortestPath(), we need to refactor that code
424 // to avoid closing the transaction.
425 //
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700426 DataPath dataPath =
427 topoRouteService.getTopoShortestPath(srcSwitchPort,
428 dstSwitchPort);
Pavlin Radoslavov4a325822013-04-02 22:16:59 +0000429 if (dataPath == null) {
430 // We need the DataPath to compare the paths
431 dataPath = new DataPath();
432 dataPath.setSrcPort(srcSwitchPort);
433 dataPath.setDstPort(dstSwitchPort);
434 }
435
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700436 String newDataPathSummaryStr = dataPath.dataPathSummary();
437 if (dataPathSummaryStr.equals(newDataPathSummaryStr))
438 continue; // Nothing changed
439
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700440 log.debug("RECONCILE: Need to Reconcile Shortest Path for FlowID {}",
441 flowId.toString());
442 flowObjSet.add(flowPathObj);
443 }
Pavlin Radoslavov53a3a8c2013-04-04 04:34:50 -0700444 log.debug("MEASUREMENT: Found {} Flows to reconcile",
Pavlin Radoslavove87f94e2013-04-04 04:31:09 -0700445 flowObjSet.size());
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700446 reconcileFlows(flowObjSet);
447 topoRouteService.dropShortestPathTopo();
Pavlin Radoslavov0391b9d2013-03-29 11:54:25 -0700448
449
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800450 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700451
452 if (processed_measurement_flow) {
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700453 long estimatedTime =
454 System.nanoTime() - modifiedMeasurementFlowTime;
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700455 String logMsg = "MEASUREMENT: Pushed Flow delay: " +
456 (double)estimatedTime / 1000000000 + " sec";
457 log.debug(logMsg);
458 }
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700459
460 long estimatedTime = System.nanoTime() - startTime;
Pavlin Radoslavov1552f952013-04-04 17:51:22 -0700461 double rate = 0.0;
462 if (estimatedTime > 0)
463 rate = ((double)counterAllFlowPaths * 1000000000) / estimatedTime;
464 String logMsg = "MEASUREMENT: Processed AllFlowEntries: " +
465 counterAllFlowEntries + " MyNotUpdatedFlowEntries: " +
466 counterMyNotUpdatedFlowEntries + " AllFlowPaths: " +
467 counterAllFlowPaths + " MyFlowPaths: " +
468 counterMyFlowPaths + " in " +
469 (double)estimatedTime / 1000000000 + " sec: " +
470 rate + " paths/s";
Pavlin Radoslavov42f02ba2013-04-03 20:07:30 -0700471 log.debug(logMsg);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800472 }
473 };
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700474
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700475 final ScheduledFuture<?> mapReaderHandle =
476 mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800477
478 @Override
479 public void init(String conf) {
480 conn = GraphDBConnection.getInstance(conf);
481 }
482
483 public void finalize() {
484 close();
485 }
486
487 @Override
488 public void close() {
489 conn.close();
490 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800491
492 @Override
493 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
494 Collection<Class<? extends IFloodlightService>> l =
495 new ArrayList<Class<? extends IFloodlightService>>();
496 l.add(IFlowService.class);
497 return l;
498 }
499
500 @Override
501 public Map<Class<? extends IFloodlightService>, IFloodlightService>
502 getServiceImpls() {
503 Map<Class<? extends IFloodlightService>,
504 IFloodlightService> m =
505 new HashMap<Class<? extends IFloodlightService>,
506 IFloodlightService>();
507 m.put(IFlowService.class, this);
508 return m;
509 }
510
511 @Override
512 public Collection<Class<? extends IFloodlightService>>
513 getModuleDependencies() {
514 Collection<Class<? extends IFloodlightService>> l =
515 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800516 l.add(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700517 l.add(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800518 l.add(IRestApiService.class);
519 return l;
520 }
521
522 @Override
523 public void init(FloodlightModuleContext context)
524 throws FloodlightModuleException {
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700525 this.context = context;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800526 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -0700527 topoRouteService = context.getServiceImpl(ITopoRouteService.class);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800528 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800529 messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
530 EnumSet.of(OFType.FLOW_MOD),
531 OFMESSAGE_DAMPER_TIMEOUT);
532 // TODO: An ugly hack!
533 String conf = "/tmp/cassandra.titan";
534 this.init(conf);
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800535 }
536
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000537 private long getNextFlowEntryId() {
538 //
539 // Generate the next Flow Entry ID.
540 // NOTE: For now, the higher 32 bits are random, and
541 // the lower 32 bits are sequential.
542 // In the future, we need a better allocation mechanism.
543 //
544 if ((nextFlowEntryIdSuffix & 0xffffffffL) == 0xffffffffL) {
545 nextFlowEntryIdPrefix = randomGenerator.nextInt();
546 nextFlowEntryIdSuffix = 0;
547 } else {
548 nextFlowEntryIdSuffix++;
549 }
550 long result = (long)nextFlowEntryIdPrefix << 32;
551 result = result | (0xffffffffL & nextFlowEntryIdSuffix);
552 return result;
553 }
554
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800555 @Override
556 public void startUp(FloodlightModuleContext context) {
557 restApi.addRestletRoutable(new FlowWebRoutable());
Pavlin Radoslavov80ca6302013-03-20 02:08:09 -0700558
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000559 // Initialize the Flow Entry ID generator
560 nextFlowEntryIdPrefix = randomGenerator.nextInt();
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800561 }
562
563 /**
564 * Add a flow.
565 *
566 * Internally, ONOS will automatically register the installer for
567 * receiving Flow Path Notifications for that path.
568 *
569 * @param flowPath the Flow Path to install.
570 * @param flowId the return-by-reference Flow ID as assigned internally.
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700571 * @param dataPathSummaryStr the data path summary string if the added
572 * flow will be maintained internally, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800573 * @return true on success, otherwise false.
574 */
575 @Override
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700576 public boolean addFlow(FlowPath flowPath, FlowId flowId,
577 String dataPathSummaryStr) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700578 /*
579 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700580 if (flowPath.flowId().value() == measurementFlowId) {
581 modifiedMeasurementFlowTime = System.nanoTime();
582 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700583 */
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800584
585 //
586 // Assign the FlowEntry IDs
Pavlin Radoslavov01391c92013-03-14 17:13:21 -0700587 // Right now every new flow entry gets a new flow entry ID
588 // TODO: This needs to be redesigned!
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800589 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800590 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
Pavlin Radoslavov0b22d0e2013-04-02 01:12:46 +0000591 long id = getNextFlowEntryId();
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800592 flowEntry.setFlowEntryId(new FlowEntryId(id));
593 }
594
595 IFlowPath flowObj = null;
596 try {
597 if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
598 != null) {
599 log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
600 flowPath.flowId().toString());
601 } else {
602 flowObj = conn.utils().newFlowPath(conn);
603 log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
604 flowPath.flowId().toString());
605 }
606 } catch (Exception e) {
607 // TODO: handle exceptions
608 conn.endTx(Transaction.ROLLBACK);
609 log.error(":addFlow FlowId:{} failed",
610 flowPath.flowId().toString());
611 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700612 if (flowObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000613 log.error(":addFlow FlowId:{} failed: Flow object not created",
614 flowPath.flowId().toString());
615 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800616 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700617 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800618
619 //
620 // Set the Flow key:
621 // - flowId
622 //
623 flowObj.setFlowId(flowPath.flowId().toString());
624 flowObj.setType("flow");
625
626 //
627 // Set the Flow attributes:
628 // - flowPath.installerId()
629 // - flowPath.dataPath().srcPort()
630 // - flowPath.dataPath().dstPort()
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700631 // - flowPath.matchEthernetFrameType()
632 // - flowPath.matchSrcIPv4Net()
633 // - flowPath.matchDstIPv4Net()
634 // - flowPath.matchSrcMac()
635 // - flowPath.matchDstMac()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800636 //
637 flowObj.setInstallerId(flowPath.installerId().toString());
638 flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
639 flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
640 flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
641 flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -0700642 if (flowPath.flowEntryMatch().matchEthernetFrameType()) {
643 flowObj.setMatchEthernetFrameType(flowPath.flowEntryMatch().ethernetFrameType());
644 }
645 if (flowPath.flowEntryMatch().matchSrcIPv4Net()) {
646 flowObj.setMatchSrcIPv4Net(flowPath.flowEntryMatch().srcIPv4Net().toString());
647 }
648 if (flowPath.flowEntryMatch().matchDstIPv4Net()) {
649 flowObj.setMatchDstIPv4Net(flowPath.flowEntryMatch().dstIPv4Net().toString());
650 }
651 if (flowPath.flowEntryMatch().matchSrcMac()) {
652 flowObj.setMatchSrcMac(flowPath.flowEntryMatch().srcMac().toString());
653 }
654 if (flowPath.flowEntryMatch().matchDstMac()) {
655 flowObj.setMatchDstMac(flowPath.flowEntryMatch().dstMac().toString());
656 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800657
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -0700658 if (dataPathSummaryStr != null) {
659 flowObj.setDataPathSummary(dataPathSummaryStr);
660 } else {
661 flowObj.setDataPathSummary("");
662 }
663
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800664 // Flow edges:
665 // HeadFE
666
667
668 //
669 // Flow Entries:
670 // flowPath.dataPath().flowEntries()
671 //
672 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
673 IFlowEntry flowEntryObj = null;
674 boolean found = false;
675 try {
676 if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
677 log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
678 flowEntry.flowEntryId().toString());
679 found = true;
680 } else {
681 flowEntryObj = conn.utils().newFlowEntry(conn);
682 log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
683 flowEntry.flowEntryId().toString());
684 }
685 } catch (Exception e) {
686 // TODO: handle exceptions
687 conn.endTx(Transaction.ROLLBACK);
688 log.error(":addFlow FlowEntryId:{} failed",
689 flowEntry.flowEntryId().toString());
690 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700691 if (flowEntryObj == null) {
Masayoshi Kobayashic9da09e2013-03-26 20:52:02 +0000692 log.error(":addFlow FlowEntryId:{} failed: FlowEntry object not created",
693 flowEntry.flowEntryId().toString());
694 conn.endTx(Transaction.ROLLBACK);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800695 return false;
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700696 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800697
698 //
699 // Set the Flow Entry key:
700 // - flowEntry.flowEntryId()
701 //
702 flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
703 flowEntryObj.setType("flow_entry");
704
705 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700706 // Set the Flow Entry Edges and attributes:
707 // - Switch edge
708 // - InPort edge
709 // - OutPort edge
710 //
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800711 // - flowEntry.flowEntryMatch()
712 // - flowEntry.flowEntryActions()
713 // - flowEntry.dpid()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800714 // - flowEntry.flowEntryUserState()
715 // - flowEntry.flowEntrySwitchState()
716 // - flowEntry.flowEntryErrorState()
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700717 // - flowEntry.matchInPort()
718 // - flowEntry.matchEthernetFrameType()
719 // - flowEntry.matchSrcIPv4Net()
720 // - flowEntry.matchDstIPv4Net()
721 // - flowEntry.matchSrcMac()
722 // - flowEntry.matchDstMac()
723 // - flowEntry.actionOutput()
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800724 //
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700725 ISwitchObject sw =
726 conn.utils().searchSwitch(conn, flowEntry.dpid().toString());
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800727 flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
Pankaj Berded0079742013-03-27 17:53:25 -0700728 flowEntryObj.setSwitch(sw);
729 if (flowEntry.flowEntryMatch().matchInPort()) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700730 IPortObject inport =
731 conn.utils().searchPort(conn, flowEntry.dpid().toString(),
732 flowEntry.flowEntryMatch().inPort().value());
Pankaj Berded0079742013-03-27 17:53:25 -0700733 flowEntryObj.setMatchInPort(flowEntry.flowEntryMatch().inPort().value());
734 flowEntryObj.setInPort(inport);
735 }
736 if (flowEntry.flowEntryMatch().matchEthernetFrameType()) {
737 flowEntryObj.setMatchEthernetFrameType(flowEntry.flowEntryMatch().ethernetFrameType());
738 }
739 if (flowEntry.flowEntryMatch().matchSrcIPv4Net()) {
740 flowEntryObj.setMatchSrcIPv4Net(flowEntry.flowEntryMatch().srcIPv4Net().toString());
741 }
742 if (flowEntry.flowEntryMatch().matchDstIPv4Net()) {
743 flowEntryObj.setMatchDstIPv4Net(flowEntry.flowEntryMatch().dstIPv4Net().toString());
744 }
745 if (flowEntry.flowEntryMatch().matchSrcMac()) {
746 flowEntryObj.setMatchSrcMac(flowEntry.flowEntryMatch().srcMac().toString());
747 }
748 if (flowEntry.flowEntryMatch().matchDstMac()) {
749 flowEntryObj.setMatchDstMac(flowEntry.flowEntryMatch().dstMac().toString());
750 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700751
752 for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
Pankaj Berded0079742013-03-27 17:53:25 -0700753 if (fa.actionOutput() != null) {
Pavlin Radoslavovb6d1e362013-03-28 03:15:41 -0700754 IPortObject outport =
755 conn.utils().searchPort(conn,
756 flowEntry.dpid().toString(),
757 fa.actionOutput().port().value());
758 flowEntryObj.setActionOutput(fa.actionOutput().port().value());
759 flowEntryObj.setOutPort(outport);
Pankaj Berded0079742013-03-27 17:53:25 -0700760 }
Pavlin Radoslavove2f0de82013-03-12 01:39:30 -0700761 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800762 // TODO: Hacks with hard-coded state names!
763 if (found)
764 flowEntryObj.setUserState("FE_USER_MODIFY");
765 else
766 flowEntryObj.setUserState("FE_USER_ADD");
767 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
768 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700769 // TODO: Take care of the FlowEntryErrorState.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800770 //
771
772 // Flow Entries edges:
773 // Flow
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700774 // NextFE (TODO)
775 if (! found) {
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800776 flowObj.addFlowEntry(flowEntryObj);
Pavlin Radoslavove0575292013-03-28 05:35:25 -0700777 flowEntryObj.setFlow(flowObj);
778 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800779 }
780 conn.endTx(Transaction.COMMIT);
781
782 //
783 // TODO: We need a proper Flow ID allocation mechanism.
784 //
785 flowId.setValue(flowPath.flowId().value());
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700786
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800787 return true;
788 }
789
790 /**
791 * Delete a previously added flow.
792 *
793 * @param flowId the Flow ID of the flow to delete.
794 * @return true on success, otherwise false.
795 */
796 @Override
797 public boolean deleteFlow(FlowId flowId) {
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700798 /*
799 * TODO: Commented-out for now
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700800 if (flowId.value() == measurementFlowId) {
801 modifiedMeasurementFlowTime = System.nanoTime();
802 }
Pavlin Radoslavov7e154fd2013-04-04 11:15:37 -0700803 */
Pavlin Radoslavov571cff92013-03-20 02:01:32 -0700804
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800805 IFlowPath flowObj = null;
806 //
807 // We just mark the entries for deletion,
808 // and let the switches remove each individual entry after
809 // it has been removed from the switches.
810 //
811 try {
812 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
813 != null) {
814 log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
815 flowId.toString());
816 } else {
817 log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
818 flowId.toString());
819 }
820 } catch (Exception e) {
821 // TODO: handle exceptions
822 conn.endTx(Transaction.ROLLBACK);
823 log.error(":deleteFlow FlowId:{} failed", flowId.toString());
824 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700825 if (flowObj == null) {
826 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800827 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700828 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800829
830 //
831 // Find and mark for deletion all Flow Entries
832 //
833 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
834 boolean empty = true; // TODO: an ugly hack
835 for (IFlowEntry flowEntryObj : flowEntries) {
836 empty = false;
837 // flowObj.removeFlowEntry(flowEntryObj);
838 // conn.utils().removeFlowEntry(conn, flowEntryObj);
839 flowEntryObj.setUserState("FE_USER_DELETE");
840 flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
841 }
842 // Remove from the database empty flows
843 if (empty)
844 conn.utils().removeFlowPath(conn, flowObj);
845 conn.endTx(Transaction.COMMIT);
846
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800847 return true;
848 }
849
850 /**
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700851 * Clear the state for a previously added flow.
852 *
853 * @param flowId the Flow ID of the flow to clear.
854 * @return true on success, otherwise false.
855 */
856 @Override
857 public boolean clearFlow(FlowId flowId) {
858 IFlowPath flowObj = null;
859 try {
860 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
861 != null) {
862 log.debug("Clearing FlowPath with FlowId {}: found existing FlowPath",
863 flowId.toString());
864 } else {
865 log.debug("Clearing FlowPath with FlowId {}: FlowPath not found",
866 flowId.toString());
867 }
868 } catch (Exception e) {
869 // TODO: handle exceptions
870 conn.endTx(Transaction.ROLLBACK);
871 log.error(":clearFlow FlowId:{} failed", flowId.toString());
872 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700873 if (flowObj == null) {
874 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700875 return true; // OK: No such flow
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700876 }
Pavlin Radoslavov916832f2013-03-14 17:48:41 -0700877
878 //
879 // Remove all Flow Entries
880 //
881 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
882 for (IFlowEntry flowEntryObj : flowEntries) {
883 flowObj.removeFlowEntry(flowEntryObj);
884 conn.utils().removeFlowEntry(conn, flowEntryObj);
885 }
886 // Remove the Flow itself
887 conn.utils().removeFlowPath(conn, flowObj);
888 conn.endTx(Transaction.COMMIT);
889
890 return true;
891 }
892
893 /**
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800894 * Get a previously added flow.
895 *
896 * @param flowId the Flow ID of the flow to get.
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800897 * @return the Flow Path if found, otherwise null.
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -0800898 */
899 @Override
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800900 public FlowPath getFlow(FlowId flowId) {
901 IFlowPath flowObj = null;
902 try {
903 if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
904 != null) {
905 log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
906 flowId.toString());
907 } else {
908 log.debug("Get FlowPath with FlowId {}: FlowPath not found",
909 flowId.toString());
910 }
911 } catch (Exception e) {
912 // TODO: handle exceptions
913 conn.endTx(Transaction.ROLLBACK);
914 log.error(":getFlow FlowId:{} failed", flowId.toString());
915 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700916 if (flowObj == null) {
917 conn.endTx(Transaction.COMMIT);
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800918 return null; // Flow not found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -0700919 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -0800920
921 //
922 // Extract the Flow state
923 //
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800924 FlowPath flowPath = extractFlowPath(flowObj);
925 conn.endTx(Transaction.COMMIT);
926
927 return flowPath;
928 }
929
930 /**
931 * Get all previously added flows by a specific installer for a given
932 * data path endpoints.
933 *
934 * @param installerId the Caller ID of the installer of the flow to get.
935 * @param dataPathEndpoints the data path endpoints of the flow to get.
936 * @return the Flow Paths if found, otherwise null.
937 */
938 @Override
939 public ArrayList<FlowPath> getAllFlows(CallerId installerId,
940 DataPathEndpoints dataPathEndpoints) {
941 //
942 // TODO: The implementation below is not optimal:
943 // We fetch all flows, and then return only the subset that match
944 // the query conditions.
945 // We should use the appropriate Titan/Gremlin query to filter-out
946 // the flows as appropriate.
947 //
948 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700949 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800950
951 if (allFlows == null) {
952 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700953 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800954 }
955
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800956 for (FlowPath flow : allFlows) {
957 //
958 // TODO: String-based comparison is sub-optimal.
959 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -0800960 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800961 //
962 if (! flow.installerId().toString().equals(installerId.toString()))
963 continue;
964 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
965 continue;
966 }
967 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
968 continue;
969 }
970 flowPaths.add(flow);
971 }
972
973 if (flowPaths.isEmpty()) {
974 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800975 } else {
976 log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
977 }
978
979 return flowPaths;
980 }
981
982 /**
983 * Get all installed flows by all installers for given data path endpoints.
984 *
985 * @param dataPathEndpoints the data path endpoints of the flows to get.
986 * @return the Flow Paths if found, otherwise null.
987 */
988 @Override
989 public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
990 //
991 // TODO: The implementation below is not optimal:
992 // We fetch all flows, and then return only the subset that match
993 // the query conditions.
994 // We should use the appropriate Titan/Gremlin query to filter-out
995 // the flows as appropriate.
996 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -0700997 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
998 ArrayList<FlowPath> allFlows = getAllFlows();
Pavlin Radoslavov706df052013-03-06 10:49:07 -0800999
1000 if (allFlows == null) {
1001 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001002 return flowPaths;
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001003 }
1004
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001005 for (FlowPath flow : allFlows) {
1006 //
1007 // TODO: String-based comparison is sub-optimal.
1008 // We are using it for now to save us the extra work of
Pavlin Radoslavovc4e76a62013-03-06 10:52:41 -08001009 // implementing the "equals()" and "hashCode()" methods.
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001010 //
1011 if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
1012 continue;
1013 }
1014 if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
1015 continue;
1016 }
1017 flowPaths.add(flow);
1018 }
1019
1020 if (flowPaths.isEmpty()) {
1021 log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001022 } else {
1023 log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
1024 }
1025
1026 return flowPaths;
1027 }
1028
1029 /**
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001030 * Get summary of all installed flows by all installers in a given range
1031 *
1032 * @param flowId the data path endpoints of the flows to get.
1033 * @param maxFlows: the maximum number of flows to be returned
1034 * @return the Flow Paths if found, otherwise null.
1035 */
1036 @Override
1037 public ArrayList<FlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
1038 //
1039 // TODO: The implementation below is not optimal:
1040 // We fetch all flows, and then return only the subset that match
1041 // the query conditions.
1042 // We should use the appropriate Titan/Gremlin query to filter-out
1043 // the flows as appropriate.
1044 //
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001045 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
1046
1047 ArrayList<FlowPath> allFlows = getAllFlows();
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001048
1049 if (allFlows == null) {
1050 log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001051 return flowPaths;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001052 }
1053
Umesh Krishnaswamy244b4ae2013-03-29 12:05:15 -07001054 Collections.sort(allFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001055
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001056 for (FlowPath flow : allFlows) {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001057 flow.setFlowEntryMatch(null);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001058
1059 // start from desired flowId
Umesh Krishnaswamy2a82c642013-03-29 08:27:17 -07001060 //if (flow.flowId().value() < flowId.value()) {
1061 // continue;
1062 //}
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001063
1064 // Summarize by making null flow entry fields that are not relevant to report
1065 for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
1066 flowEntry.setFlowEntryActions(null);
1067 flowEntry.setFlowEntryMatch(null);
1068 }
1069
1070 flowPaths.add(flow);
1071 if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
1072 break;
1073 }
1074 }
1075
1076 if (flowPaths.isEmpty()) {
1077 log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -07001078 } else {
1079 log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
1080 }
1081
1082 return flowPaths;
1083 }
1084
1085 /**
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001086 * Get all installed flows by all installers.
1087 *
1088 * @return the Flow Paths if found, otherwise null.
1089 */
1090 @Override
1091 public ArrayList<FlowPath> getAllFlows() {
1092 Iterable<IFlowPath> flowPathsObj = null;
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001093 ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001094
1095 try {
1096 if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
1097 log.debug("Get all FlowPaths: found FlowPaths");
1098 } else {
1099 log.debug("Get all FlowPaths: no FlowPaths found");
1100 }
1101 } catch (Exception e) {
1102 // TODO: handle exceptions
1103 conn.endTx(Transaction.ROLLBACK);
1104 log.error(":getAllFlowPaths failed");
1105 }
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001106 if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false)) {
1107 conn.endTx(Transaction.COMMIT);
Umesh Krishnaswamyea0f4ab2013-03-26 18:49:35 -07001108 return flowPaths; // No Flows found
Pavlin Radoslavov89c8f432013-03-15 18:50:46 -07001109 }
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001110
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001111 for (IFlowPath flowObj : flowPathsObj) {
1112 //
1113 // Extract the Flow state
1114 //
1115 FlowPath flowPath = extractFlowPath(flowObj);
Pavlin Radoslavov3f2af732013-03-29 15:29:35 -07001116 if (flowPath != null)
1117 flowPaths.add(flowPath);
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001118 }
1119
1120 conn.endTx(Transaction.COMMIT);
1121
1122 return flowPaths;
1123 }
1124
1125 /**
1126 * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
1127 *
1128 * @param flowObj the object to extract the Flow Path State from.
1129 * @return the extracted Flow Path State.
1130 */
1131 private FlowPath extractFlowPath(IFlowPath flowObj) {
Pavlin Radoslavov706df052013-03-06 10:49:07 -08001132 //
1133 // Extract the Flow state
1134 //
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001135 String flowIdStr = flowObj.getFlowId();
1136 String installerIdStr = flowObj.getInstallerId();
1137 String srcSwitchStr = flowObj.getSrcSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001138 Short srcPortShort = flowObj.getSrcPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001139 String dstSwitchStr = flowObj.getDstSwitch();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001140 Short dstPortShort = flowObj.getDstPort();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001141
1142 if ((flowIdStr == null) ||
1143 (installerIdStr == null) ||
1144 (srcSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001145 (srcPortShort == null) ||
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001146 (dstSwitchStr == null) ||
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001147 (dstPortShort == null)) {
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001148 // TODO: A work-around, becauuse of some bogus database objects
1149 return null;
1150 }
1151
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001152 FlowPath flowPath = new FlowPath();
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001153 flowPath.setFlowId(new FlowId(flowIdStr));
1154 flowPath.setInstallerId(new CallerId(installerIdStr));
1155 flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001156 flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001157 flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001158 flowPath.dataPath().dstPort().setPort(new Port(dstPortShort));
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001159 //
1160 // Extract the match conditions common for all Flow Entries
1161 //
1162 {
1163 FlowEntryMatch match = new FlowEntryMatch();
1164 Short matchEthernetFrameType = flowObj.getMatchEthernetFrameType();
1165 if (matchEthernetFrameType != null)
1166 match.enableEthernetFrameType(matchEthernetFrameType);
1167 String matchSrcIPv4Net = flowObj.getMatchSrcIPv4Net();
1168 if (matchSrcIPv4Net != null)
1169 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1170 String matchDstIPv4Net = flowObj.getMatchDstIPv4Net();
1171 if (matchDstIPv4Net != null)
1172 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1173 String matchSrcMac = flowObj.getMatchSrcMac();
1174 if (matchSrcMac != null)
1175 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1176 String matchDstMac = flowObj.getMatchDstMac();
1177 if (matchDstMac != null)
1178 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1179 flowPath.setFlowEntryMatch(match);
1180 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001181
1182 //
1183 // Extract all Flow Entries
1184 //
1185 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
1186 for (IFlowEntry flowEntryObj : flowEntries) {
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001187 FlowEntry flowEntry = extractFlowEntry(flowEntryObj);
1188 if (flowEntry == null)
Pavlin Radoslavovc2877682013-03-27 16:40:07 -07001189 continue;
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001190 flowPath.dataPath().flowEntries().add(flowEntry);
1191 }
Pavlin Radoslavovb6f53542013-03-01 16:02:14 -08001192
1193 return flowPath;
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001194 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001195
1196 /**
Pavlin Radoslavov99b12752013-04-04 17:28:06 -07001197 * Extract Flow Entry State from a Titan Database Object @ref IFlowEntry.
1198 *
1199 * @param flowEntryObj the object to extract the Flow Entry State from.
1200 * @return the extracted Flow Entry State.
1201 */
1202 private FlowEntry extractFlowEntry(IFlowEntry flowEntryObj) {
1203 String flowEntryIdStr = flowEntryObj.getFlowEntryId();
1204 String switchDpidStr = flowEntryObj.getSwitchDpid();
1205 String userState = flowEntryObj.getUserState();
1206 String switchState = flowEntryObj.getSwitchState();
1207
1208 if ((flowEntryIdStr == null) ||
1209 (switchDpidStr == null) ||
1210 (userState == null) ||
1211 (switchState == null)) {
1212 // TODO: A work-around, becauuse of some bogus database objects
1213 return null;
1214 }
1215
1216 FlowEntry flowEntry = new FlowEntry();
1217 flowEntry.setFlowEntryId(new FlowEntryId(flowEntryIdStr));
1218 flowEntry.setDpid(new Dpid(switchDpidStr));
1219
1220 //
1221 // Extract the match conditions
1222 //
1223 FlowEntryMatch match = new FlowEntryMatch();
1224 Short matchInPort = flowEntryObj.getMatchInPort();
1225 if (matchInPort != null)
1226 match.enableInPort(new Port(matchInPort));
1227 Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
1228 if (matchEthernetFrameType != null)
1229 match.enableEthernetFrameType(matchEthernetFrameType);
1230 String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
1231 if (matchSrcIPv4Net != null)
1232 match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
1233 String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
1234 if (matchDstIPv4Net != null)
1235 match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
1236 String matchSrcMac = flowEntryObj.getMatchSrcMac();
1237 if (matchSrcMac != null)
1238 match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
1239 String matchDstMac = flowEntryObj.getMatchDstMac();
1240 if (matchDstMac != null)
1241 match.enableDstMac(MACAddress.valueOf(matchDstMac));
1242 flowEntry.setFlowEntryMatch(match);
1243
1244 //
1245 // Extract the actions
1246 //
1247 ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
1248 Short actionOutputPort = flowEntryObj.getActionOutput();
1249 if (actionOutputPort != null) {
1250 FlowEntryAction action = new FlowEntryAction();
1251 action.setActionOutput(new Port(actionOutputPort));
1252 actions.add(action);
1253 }
1254 flowEntry.setFlowEntryActions(actions);
1255 flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
1256 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
1257 //
1258 // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
1259 // and FlowEntryErrorState.
1260 //
1261 return flowEntry;
1262 }
1263
1264 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001265 * Add and maintain a shortest-path flow.
1266 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001267 * NOTE: The Flow Path argument does NOT contain flow entries.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001268 *
1269 * @param flowPath the Flow Path with the endpoints and the match
1270 * conditions to install.
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001271 * @return the added shortest-path flow on success, otherwise null.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001272 */
1273 @Override
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001274 public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath) {
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001275 String dataPathSummaryStr = null;
1276
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001277 //
1278 // Do the shortest path computation
1279 //
1280 DataPath dataPath =
1281 topoRouteService.getShortestPath(flowPath.dataPath().srcPort(),
1282 flowPath.dataPath().dstPort());
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001283 if (dataPath == null) {
1284 // We need the DataPath to populate the Network MAP
1285 dataPath = new DataPath();
1286 dataPath.setSrcPort(flowPath.dataPath().srcPort());
1287 dataPath.setDstPort(flowPath.dataPath().dstPort());
1288 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001289
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001290 // Compute the Data Path summary
1291 dataPathSummaryStr = dataPath.dataPathSummary();
1292
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001293 //
1294 // Set the incoming port matching and the outgoing port output
1295 // actions for each flow entry.
1296 //
1297 for (FlowEntry flowEntry : dataPath.flowEntries()) {
1298 // Set the incoming port matching
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001299 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001300 flowEntry.setFlowEntryMatch(flowEntryMatch);
1301 flowEntryMatch.enableInPort(flowEntry.inPort());
1302
1303 // Set the outgoing port output action
1304 ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
1305 if (flowEntryActions == null) {
1306 flowEntryActions = new ArrayList<FlowEntryAction>();
1307 flowEntry.setFlowEntryActions(flowEntryActions);
1308 }
1309 FlowEntryAction flowEntryAction = new FlowEntryAction();
1310 flowEntryAction.setActionOutput(flowEntry.outPort());
1311 flowEntryActions.add(flowEntryAction);
1312 }
1313
1314 //
1315 // Prepare the computed Flow Path
1316 //
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001317 FlowPath computedFlowPath = new FlowPath();
1318 computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
1319 computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
1320 computedFlowPath.setDataPath(dataPath);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001321 computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001322
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001323 FlowId flowId = new FlowId();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001324 if (! addFlow(computedFlowPath, flowId, dataPathSummaryStr))
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001325 return null;
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001326
1327 // TODO: Mark the flow for maintenance purpose
1328
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001329 return (computedFlowPath);
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001330 }
1331
1332 /**
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001333 * Reconcile all flows in a set.
1334 *
1335 * @param flowObjSet the set of flows that need to be reconciliated.
1336 */
1337 public void reconcileFlows(Iterable<IFlowPath> flowObjSet) {
1338 if (! flowObjSet.iterator().hasNext())
1339 return;
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001340
1341 //
1342 // Remove the old Flow Entries, and add the new Flow Entries
1343 //
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001344
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001345 Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001346 LinkedList<FlowPath> flowPaths = new LinkedList<FlowPath>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001347 for (IFlowPath flowObj : flowObjSet) {
1348 FlowPath flowPath = extractFlowPath(flowObj);
1349 if (flowPath == null)
1350 continue;
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001351 flowPaths.add(flowPath);
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001352
1353 //
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001354 // Remove the Flow Entries from the Network MAP
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001355 //
1356 Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001357 LinkedList<IFlowEntry> deleteFlowEntries = new LinkedList<IFlowEntry>();
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001358 for (IFlowEntry flowEntryObj : flowEntries) {
1359 String dpidStr = flowEntryObj.getSwitchDpid();
1360 if (dpidStr == null)
1361 continue;
1362 Dpid dpid = new Dpid(dpidStr);
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001363 /*
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001364 IOFSwitch mySwitch = mySwitches.get(dpid.value());
1365 if (mySwitch == null)
1366 continue; // Ignore the entry: not my switch
Pavlin Radoslavovb2b6d4f2013-04-01 21:27:31 +00001367 */
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001368 deleteFlowEntries.add(flowEntryObj);
1369 }
1370 for (IFlowEntry flowEntryObj : deleteFlowEntries) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001371 flowObj.removeFlowEntry(flowEntryObj);
1372 conn.utils().removeFlowEntry(conn, flowEntryObj);
1373 }
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001374 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001375
Pavlin Radoslavovdbaaf2e2013-03-29 04:25:55 -07001376 for (FlowPath flowPath : flowPaths) {
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001377 //
1378 // Delete the flow entries from the switches
1379 //
1380 for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
1381 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001382 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1383 if (mySwitch == null) {
1384 // Not my switch
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001385 installRemoteFlowEntry(flowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001386 } else {
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001387 installFlowEntry(mySwitch, flowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001388 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001389 }
1390
1391 //
1392 // Calculate the new shortest path and install it in the
1393 // Network MAP.
1394 //
1395 FlowPath addedFlowPath = addAndMaintainShortestPathFlow(flowPath);
1396 if (addedFlowPath == null) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001397 log.error("Cannot add Shortest Path Flow from {} to {}: path not found?",
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001398 flowPath.dataPath().srcPort().toString(),
1399 flowPath.dataPath().dstPort().toString());
1400 continue;
1401 }
1402
1403 //
1404 // Add the flow entries to the switches
1405 //
1406 for (FlowEntry flowEntry : addedFlowPath.dataPath().flowEntries()) {
1407 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001408 IOFSwitch mySwitch = mySwitches.get(flowEntry.dpid().value());
1409 if (mySwitch == null) {
1410 // Not my switch
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001411 installRemoteFlowEntry(addedFlowPath, flowEntry);
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001412 continue;
1413 }
1414
1415 IFlowEntry flowEntryObj =
1416 conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId());
1417 if (flowEntryObj == null) {
1418 //
1419 // TODO: Remove the "new Object[] wrapper in the statement
1420 // below after the SLF4J logger is upgraded to
1421 // Version 1.7.5
1422 //
1423 log.error("Cannot add Flow Entry to switch {} for Path Flow from {} to {} : Flow Entry not in the Network MAP",
1424 new Object[] {
1425 flowEntry.dpid(),
1426 flowPath.dataPath().srcPort(),
1427 flowPath.dataPath().dstPort()
1428 });
1429 continue;
1430 }
1431 // Update the Flow Entry Switch State in the Network MAP
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001432 if (installFlowEntry(mySwitch, addedFlowPath, flowEntry)) {
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001433 flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
1434 }
Pavlin Radoslavove0575292013-03-28 05:35:25 -07001435 }
1436 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001437 }
1438
1439 /**
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001440 * Install a Flow Entry on a switch.
1441 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001442 * @param mySwitch the switch to install the Flow Entry into.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001443 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001444 * @param flowEntry the flow entry to install.
1445 * @return true on success, otherwise false.
1446 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001447 public boolean installFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1448 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001449 //
1450 // Create the OpenFlow Flow Modification Entry to push
1451 //
1452 OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
1453 .getMessage(OFType.FLOW_MOD);
1454 long cookie = flowEntry.flowEntryId().value();
1455
1456 short flowModCommand = OFFlowMod.OFPFC_ADD;
1457 if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
1458 flowModCommand = OFFlowMod.OFPFC_ADD;
1459 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
1460 flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
1461 } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
1462 flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
1463 } else {
1464 // Unknown user state. Ignore the entry
1465 log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
1466 flowEntry.flowEntryId().toString(),
1467 flowEntry.flowEntryUserState());
1468 return false;
1469 }
1470
1471 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001472 // Fetch the match conditions.
1473 //
1474 // NOTE: The Flow matching conditions common for all Flow Entries are
1475 // used ONLY if a Flow Entry does NOT have the corresponding matching
1476 // condition set.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001477 //
1478 OFMatch match = new OFMatch();
1479 match.setWildcards(OFMatch.OFPFW_ALL);
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001480 FlowEntryMatch flowPathMatch = flowPath.flowEntryMatch();
1481 FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
1482
1483 // Match the Incoming Port
1484 Port matchInPort = flowEntryMatch.inPort();
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001485 if (matchInPort != null) {
1486 match.setInputPort(matchInPort.value());
1487 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
1488 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001489
1490 // Match the Ethernet Frame Type
1491 Short matchEthernetFrameType = flowEntryMatch.ethernetFrameType();
1492 if ((matchEthernetFrameType == null) && (flowPathMatch != null)) {
1493 matchEthernetFrameType = flowPathMatch.ethernetFrameType();
1494 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001495 if (matchEthernetFrameType != null) {
1496 match.setDataLayerType(matchEthernetFrameType);
1497 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
1498 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001499
1500 // Match the Source IPv4 Network prefix
1501 IPv4Net matchSrcIPv4Net = flowEntryMatch.srcIPv4Net();
1502 if ((matchSrcIPv4Net == null) && (flowPathMatch != null)) {
1503 matchSrcIPv4Net = flowPathMatch.srcIPv4Net();
1504 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001505 if (matchSrcIPv4Net != null) {
1506 match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
1507 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001508
1509 // Natch the Destination IPv4 Network prefix
1510 IPv4Net matchDstIPv4Net = flowEntryMatch.dstIPv4Net();
1511 if ((matchDstIPv4Net == null) && (flowPathMatch != null)) {
1512 matchDstIPv4Net = flowPathMatch.dstIPv4Net();
1513 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001514 if (matchDstIPv4Net != null) {
1515 match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
1516 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001517
1518 // Match the Source MAC address
1519 MACAddress matchSrcMac = flowEntryMatch.srcMac();
1520 if ((matchSrcMac == null) && (flowPathMatch != null)) {
1521 matchSrcMac = flowPathMatch.srcMac();
1522 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001523 if (matchSrcMac != null) {
1524 match.setDataLayerSource(matchSrcMac.toString());
1525 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
1526 }
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001527
1528 // Match the Destination MAC address
1529 MACAddress matchDstMac = flowEntryMatch.dstMac();
1530 if ((matchDstMac == null) && (flowPathMatch != null)) {
1531 matchDstMac = flowPathMatch.dstMac();
1532 }
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001533 if (matchDstMac != null) {
1534 match.setDataLayerDestination(matchDstMac.toString());
1535 match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
1536 }
1537
1538 //
1539 // Fetch the actions
1540 //
1541 // TODO: For now we support only the "OUTPUT" actions.
1542 //
1543 fm.setOutPort(OFPort.OFPP_NONE.getValue());
1544 List<OFAction> actions = new ArrayList<OFAction>();
1545 ArrayList<FlowEntryAction> flowEntryActions =
1546 flowEntry.flowEntryActions();
1547 for (FlowEntryAction flowEntryAction : flowEntryActions) {
1548 FlowEntryAction.ActionOutput actionOutput =
1549 flowEntryAction.actionOutput();
1550 if (actionOutput != null) {
1551 short actionOutputPort = actionOutput.port().value();
1552 OFActionOutput action = new OFActionOutput();
1553 // XXX: The max length is hard-coded for now
1554 action.setMaxLength((short)0xffff);
1555 action.setPort(actionOutputPort);
1556 actions.add(action);
1557 if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
1558 (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
1559 fm.setOutPort(actionOutputPort);
1560 }
1561 }
1562 }
1563
1564 fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
1565 .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
1566 .setPriority(PRIORITY_DEFAULT)
1567 .setBufferId(OFPacketOut.BUFFER_ID_NONE)
1568 .setCookie(cookie)
1569 .setCommand(flowModCommand)
1570 .setMatch(match)
1571 .setActions(actions)
1572 .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
1573
1574 //
1575 // TODO: Set the following flag
1576 // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
1577 // See method ForwardingBase::pushRoute()
1578 //
1579
1580 //
1581 // Write the message to the switch
1582 //
1583 try {
1584 messageDamper.write(mySwitch, fm, null);
1585 mySwitch.flush();
1586 } catch (IOException e) {
1587 log.error("Failure writing flow mod from network map", e);
1588 return false;
1589 }
1590 return true;
1591 }
1592
1593 /**
1594 * Remove a Flow Entry from a switch.
1595 *
Pavlin Radoslavov2b858f82013-03-28 11:37:37 -07001596 * @param mySwitch the switch to remove the Flow Entry from.
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001597 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001598 * @param flowEntry the flow entry to remove.
1599 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001600 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001601 public boolean removeFlowEntry(IOFSwitch mySwitch, FlowPath flowPath,
1602 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001603 //
1604 // The installFlowEntry() method implements both installation
1605 // and removal of flow entries.
1606 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001607 return (installFlowEntry(mySwitch, flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001608 }
1609
1610 /**
1611 * Install a Flow Entry on a remote controller.
1612 *
1613 * TODO: We need it now: Jono
1614 * - For now it will make a REST call to the remote controller.
1615 * - Internally, it needs to know the name of the remote controller.
1616 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001617 * @param flowPath the flow path for the flow entry to install.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001618 * @param flowEntry the flow entry to install.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001619 * @return true on success, otherwise false.
1620 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001621 public boolean installRemoteFlowEntry(FlowPath flowPath,
1622 FlowEntry flowEntry) {
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001623 // TODO: We need it now: Jono
1624 // - For now it will make a REST call to the remote controller.
1625 // - Internally, it needs to know the name of the remote controller.
1626 return true;
1627 }
1628
1629 /**
1630 * Remove a flow entry on a remote controller.
1631 *
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001632 * @param flowPath the flow path for the flow entry to remove.
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001633 * @param flowEntry the flow entry to remove.
1634 * @return true on success, otherwise false.
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001635 */
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001636 public boolean removeRemoteFlowEntry(FlowPath flowPath,
1637 FlowEntry flowEntry) {
Pavlin Radoslavov6b6f4a82013-03-28 03:30:00 -07001638 //
1639 // The installRemoteFlowEntry() method implements both installation
1640 // and removal of flow entries.
1641 //
Pavlin Radoslavov67b3ef32013-04-03 02:44:48 -07001642 return (installRemoteFlowEntry(flowPath, flowEntry));
Pavlin Radoslavovb9fe6b42013-03-27 16:25:05 -07001643 }
Pavlin Radoslavov9e5344c2013-02-18 09:58:30 -08001644}