blob: d840353aa7232c0d3c7c3bb0e1729e788a659d89 [file] [log] [blame]
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -07001package net.onrc.onos.ofcontroller.flowmanager;
2
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -07003import java.util.ArrayList;
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -07004import java.util.Collection;
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -07005import java.util.HashMap;
6import java.util.Iterator;
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -07007import java.util.LinkedList;
8import java.util.List;
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -07009import java.util.Map;
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -070010
11import java.util.concurrent.BlockingQueue;
12import java.util.concurrent.LinkedBlockingQueue;
13
14import net.onrc.onos.datagrid.IDatagridService;
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -070015import net.onrc.onos.ofcontroller.topology.ShortestPath;
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070016import net.onrc.onos.ofcontroller.topology.Topology;
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -070017import net.onrc.onos.ofcontroller.topology.TopologyElement;
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -070018import net.onrc.onos.ofcontroller.util.DataPath;
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -070019import net.onrc.onos.ofcontroller.util.EventEntry;
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -070020import net.onrc.onos.ofcontroller.util.FlowEntry;
21import net.onrc.onos.ofcontroller.util.FlowEntryAction;
22import net.onrc.onos.ofcontroller.util.FlowEntryActions;
23import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
24import net.onrc.onos.ofcontroller.util.FlowEntrySwitchState;
25import net.onrc.onos.ofcontroller.util.FlowEntryUserState;
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -070026import net.onrc.onos.ofcontroller.util.FlowPath;
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -070027import net.onrc.onos.ofcontroller.util.FlowPathUserState;
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -070028
29import org.slf4j.Logger;
30import org.slf4j.LoggerFactory;
31
32/**
33 * Class for implementing the Path Computation and Path Maintenance.
34 */
35class PathComputation extends Thread implements IPathComputationService {
36 /** The logger. */
37 private final static Logger log = LoggerFactory.getLogger(PathComputation.class);
38
39 private FlowManager flowManager; // The Flow Manager to use
40 private IDatagridService datagridService; // The Datagrid Service to use
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070041 private Topology topology; // The network topology
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -070042 private Map<Long, FlowPath> allFlowPaths = new HashMap<Long, FlowPath>();
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -070043
44 // The queue with Flow Path and Topology Element updates
45 private BlockingQueue<EventEntry<?>> networkEvents =
46 new LinkedBlockingQueue<EventEntry<?>>();
47
48 // The pending Topology and Flow Path events
49 private List<EventEntry<TopologyElement>> topologyEvents =
50 new LinkedList<EventEntry<TopologyElement>>();
51 private List<EventEntry<FlowPath>> flowPathEvents =
52 new LinkedList<EventEntry<FlowPath>>();
53
54 /**
55 * Constructor for a given Flow Manager and Datagrid Service.
56 *
57 * @param flowManager the Flow Manager to use.
58 * @param datagridService the Datagrid Service to use.
59 */
60 PathComputation(FlowManager flowManager,
61 IDatagridService datagridService) {
62 this.flowManager = flowManager;
63 this.datagridService = datagridService;
Pavlin Radoslavova23e5412013-10-27 19:56:40 -070064 this.topology = new Topology();
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -070065 }
66
67 /**
68 * Run the thread.
69 */
70 @Override
71 public void run() {
72 //
73 // Obtain the initial Topology state
74 //
75 Collection<TopologyElement> topologyElements =
76 datagridService.getAllTopologyElements();
77 for (TopologyElement topologyElement : topologyElements) {
78 EventEntry<TopologyElement> eventEntry =
79 new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
80 topologyEvents.add(eventEntry);
81 }
82 //
83 // Obtain the initial Flow Path state
84 //
85 Collection<FlowPath> flowPaths = datagridService.getAllFlows();
86 for (FlowPath flowPath : flowPaths) {
87 EventEntry<FlowPath> eventEntry =
88 new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
89 flowPathEvents.add(eventEntry);
90 }
91 // Process the events (if any)
92 processEvents();
93
94 //
95 // The main loop
96 //
97 Collection<EventEntry<?>> collection = new LinkedList<EventEntry<?>>();
98 try {
99 while (true) {
100 EventEntry<?> eventEntry = networkEvents.take();
101 collection.add(eventEntry);
102 networkEvents.drainTo(collection);
103
Pavlin Radoslavoved4c7a92013-10-26 21:36:21 -0700104 //
105 // Demultiplex all events:
106 // - EventEntry<TopologyElement>
107 // - EventEntry<FlowPath>
108 //
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -0700109 for (EventEntry<?> event : collection) {
110 if (event.eventData() instanceof TopologyElement) {
111 EventEntry<TopologyElement> topologyEventEntry =
112 (EventEntry<TopologyElement>)event;
113 topologyEvents.add(topologyEventEntry);
114 } else if (event.eventData() instanceof FlowPath) {
115 EventEntry<FlowPath> flowPathEventEntry =
116 (EventEntry<FlowPath>)event;
117 flowPathEvents.add(flowPathEventEntry);
118 }
119 }
120 collection.clear();
Pavlin Radoslavoved4c7a92013-10-26 21:36:21 -0700121
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -0700122 // Process the events (if any)
123 processEvents();
124 }
125 } catch (Exception exception) {
126 log.debug("Exception processing Network Events: ", exception);
127 }
128 }
129
130 /**
131 * Process the events (if any)
132 */
133 private void processEvents() {
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -0700134 List<FlowPath> newFlowPaths = new LinkedList<FlowPath>();
135 List<FlowPath> recomputeFlowPaths = new LinkedList<FlowPath>();
136 List<FlowPath> modifiedFlowPaths = new LinkedList<FlowPath>();
137
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -0700138 if (topologyEvents.isEmpty() && flowPathEvents.isEmpty())
139 return; // Nothing to do
140
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700141 //
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -0700142 // Process the Flow Path events
143 //
144 for (EventEntry<FlowPath> eventEntry : flowPathEvents) {
145 FlowPath flowPath = eventEntry.eventData();
146
147 switch (eventEntry.eventType()) {
148 case ENTRY_ADD: {
149 //
150 // Add a new Flow Path
151 //
152 if (allFlowPaths.get(flowPath.flowId().value()) != null) {
153 //
154 // TODO: What to do if the Flow Path already exists?
155 // Remove and then re-add it, or merge the info?
156 // For now, we don't have to do anything.
157 //
158 break;
159 }
160
161 switch (flowPath.flowPathType()) {
162 case FP_TYPE_SHORTEST_PATH:
163 //
164 // Reset the Data Path, in case it was set already, because
165 // we are going to recompute it anyway.
166 //
167 flowPath.flowEntries().clear();
168 recomputeFlowPaths.add(flowPath);
169 break;
170 case FP_TYPE_EXPLICIT_PATH:
171 //
172 // Mark all Flow Entries for installation in the switches.
173 //
174 for (FlowEntry flowEntry : flowPath.flowEntries()) {
175 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
176 }
177 modifiedFlowPaths.add(flowPath);
178 break;
179 }
180 newFlowPaths.add(flowPath);
181
182 break;
183 }
184
185 case ENTRY_REMOVE: {
186 //
187 // Remove an existing Flow Path.
188 //
189 // Find the Flow Path, and mark the Flow and its Flow Entries
190 // for deletion.
191 //
192 FlowPath existingFlowPath =
193 allFlowPaths.get(flowPath.flowId().value());
194 if (existingFlowPath == null)
195 continue; // Nothing to do
196
197 existingFlowPath.setFlowPathUserState(FlowPathUserState.FP_USER_DELETE);
198 for (FlowEntry flowEntry : existingFlowPath.flowEntries()) {
199 flowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
200 flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
201 }
202
203 allFlowPaths.remove(existingFlowPath.flowId());
204 modifiedFlowPaths.add(existingFlowPath);
205
206 break;
207 }
208 }
209 }
210
211 //
212 // Process the topology events
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700213 //
214 boolean isTopologyModified = false;
215 for (EventEntry<TopologyElement> eventEntry : topologyEvents) {
216 TopologyElement topologyElement = eventEntry.eventData();
217 switch (eventEntry.eventType()) {
218 case ENTRY_ADD:
219 isTopologyModified = topology.addTopologyElement(topologyElement);
220 break;
221 case ENTRY_REMOVE:
222 isTopologyModified = topology.removeTopologyElement(topologyElement);
223 break;
224 }
225 }
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -0700226 if (isTopologyModified) {
227 // TODO: For now, if the topology changes, we recompute all Flows
228 recomputeFlowPaths.addAll(allFlowPaths.values());
229 }
Pavlin Radoslavova23e5412013-10-27 19:56:40 -0700230
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -0700231 // Add all new Flows
232 for (FlowPath flowPath : newFlowPaths) {
233 allFlowPaths.put(flowPath.flowId().value(), flowPath);
234 }
235
236 // Recompute all affected Flow Paths and keep only the modified
237 for (FlowPath flowPath : recomputeFlowPaths) {
238 if (recomputeFlowPath(flowPath))
239 modifiedFlowPaths.add(flowPath);
240 }
241
242 // TODO: Implement the rest: push the Flow Entries from modifiedFlowPaths!
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -0700243
Pavlin Radoslavoved4c7a92013-10-26 21:36:21 -0700244 // Cleanup
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -0700245 topologyEvents.clear();
246 flowPathEvents.clear();
247 }
248
249 /**
Pavlin Radoslavovfb06a9e2013-10-28 23:56:15 -0700250 * Recompute a Flow Path.
251 *
252 * @param flowPath the Flow Path to recompute.
253 * @return true if the recomputed Flow Path has changed, otherwise false.
254 */
255 private boolean recomputeFlowPath(FlowPath flowPath) {
256 boolean hasChanged = false;
257
258 //
259 // Test whether the Flow Path needs to be recomputed
260 //
261 switch (flowPath.flowPathType()) {
262 case FP_TYPE_SHORTEST_PATH:
263 break;
264 case FP_TYPE_EXPLICIT_PATH:
265 return false; // An explicit path never changes
266 }
267
268 DataPath oldDataPath = flowPath.dataPath();
269
270 // Compute the new shortest path
271 DataPath newDataPath =
272 ShortestPath.getTopologyShortestPath(topology,
273 flowPath.dataPath().srcPort(),
274 flowPath.dataPath().dstPort());
275 if (newDataPath == null) {
276 // We need the DataPath to compare the paths
277 newDataPath = new DataPath();
278 }
279 newDataPath.applyFlowPathFlags(flowPath.flowPathFlags());
280
281 //
282 // Test whether the shortest path is same
283 //
284 if (oldDataPath.flowEntries().size() !=
285 newDataPath.flowEntries().size()) {
286 hasChanged = true;
287 } else {
288 Iterator<FlowEntry> oldIter = oldDataPath.flowEntries().iterator();
289 Iterator<FlowEntry> newIter = newDataPath.flowEntries().iterator();
290 while (oldIter.hasNext() && newIter.hasNext()) {
291 FlowEntry oldFlowEntry = oldIter.next();
292 FlowEntry newFlowEntry = newIter.next();
293 if (! newFlowEntry.isSameDataPath(oldFlowEntry)) {
294 hasChanged = true;
295 break;
296 }
297 }
298 }
299 if (! hasChanged)
300 return hasChanged;
301
302 //
303 // Merge the changes in the shortest path:
304 // - If a Flow Entry for a switch is in the old data path, but not
305 // in the new data path, then mark it for deletion.
306 // - If a Flow Entry for a switch is in the new data path, but not
307 // in the old data path, then mark it for addition.
308 // - If a Flow Entry for a switch is in both the old and the new
309 // data path, but it has changed, e.g., the incoming and/or outgoing
310 // port(s), then mark the old Flow Entry for deletion, and mark
311 // the new Flow Entry for addition.
312 // - If a Flow Entry for a switch is in both the old and the new
313 // data path, and it hasn't changed, then just keep it.
314 //
315 // NOTE: We use the Switch DPID of each entry to match the entries
316 //
317 Map<Long, FlowEntry> oldFlowEntriesMap = new HashMap<Long, FlowEntry>();
318 Map<Long, FlowEntry> newFlowEntriesMap = new HashMap<Long, FlowEntry>();
319 ArrayList<FlowEntry> finalFlowEntries = new ArrayList<FlowEntry>();
320 List<FlowEntry> deletedFlowEntries = new LinkedList<FlowEntry>();
321
322 // Prepare maps with the Flow Entries, so they are fast to lookup
323 for (FlowEntry flowEntry : oldDataPath.flowEntries())
324 oldFlowEntriesMap.put(flowEntry.dpid().value(), flowEntry);
325 for (FlowEntry flowEntry : newDataPath.flowEntries())
326 newFlowEntriesMap.put(flowEntry.dpid().value(), flowEntry);
327
328 //
329 // Find the old Flow Entries that should be deleted
330 //
331 for (FlowEntry oldFlowEntry : oldDataPath.flowEntries()) {
332 FlowEntry newFlowEntry =
333 newFlowEntriesMap.get(oldFlowEntry.dpid().value());
334 if (newFlowEntry == null) {
335 // The old Flow Entry should be deleted
336 oldFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
337 oldFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
338 deletedFlowEntries.add(oldFlowEntry);
339 }
340 }
341
342 //
343 // Find the new Flow Entries that should be added or updated
344 //
345 int idx = 0;
346 for (FlowEntry newFlowEntry : newDataPath.flowEntries()) {
347 FlowEntry oldFlowEntry =
348 oldFlowEntriesMap.get(newFlowEntry.dpid().value());
349
350 if ((oldFlowEntry != null) &&
351 newFlowEntry.isSameDataPath(oldFlowEntry)) {
352 //
353 // Both Flow Entries are same
354 //
355 finalFlowEntries.add(oldFlowEntry);
356 idx++;
357 continue;
358 }
359
360 if (oldFlowEntry != null) {
361 //
362 // The old Flow Entry should be deleted
363 //
364 oldFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_DELETE);
365 oldFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
366 deletedFlowEntries.add(oldFlowEntry);
367 }
368
369 //
370 // Add the new Flow Entry
371 //
372
373 // Set the incoming port matching
374 FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
375 newFlowEntry.setFlowEntryMatch(flowEntryMatch);
376 flowEntryMatch.enableInPort(newFlowEntry.inPort());
377
378 //
379 // Set the actions:
380 // If the first Flow Entry, copy the Flow Path actions to it.
381 //
382 FlowEntryActions flowEntryActions = newFlowEntry.flowEntryActions();
383 if ((idx == 0) && (flowPath.flowEntryActions() != null)) {
384 FlowEntryActions flowActions =
385 new FlowEntryActions(flowPath.flowEntryActions());
386 for (FlowEntryAction action : flowActions.actions())
387 flowEntryActions.addAction(action);
388 }
389 idx++;
390
391 //
392 // Add the outgoing port output action
393 //
394 FlowEntryAction flowEntryAction = new FlowEntryAction();
395 flowEntryAction.setActionOutput(newFlowEntry.outPort());
396 flowEntryActions.addAction(flowEntryAction);
397
398 //
399 // Set the state of the new Flow Entry
400 //
401 newFlowEntry.setFlowEntryUserState(FlowEntryUserState.FE_USER_ADD);
402 newFlowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.FE_SWITCH_NOT_UPDATED);
403 finalFlowEntries.add(newFlowEntry);
404 }
405
406 //
407 // Replace the old Flow Entries with the new Flow Entries.
408 // Note that the Flow Entries that will be deleted are added at
409 // the end.
410 //
411 for (FlowEntry flowEntry : deletedFlowEntries)
412 finalFlowEntries.add(flowEntry);
413 flowPath.dataPath().setFlowEntries(finalFlowEntries);
414
415 return hasChanged;
416 }
417
418 /**
Pavlin Radoslavov6b79f2b2013-10-26 21:31:10 -0700419 * Receive a notification that a Flow is added.
420 *
421 * @param flowPath the flow that is added.
422 */
423 @Override
424 public void notificationRecvFlowAdded(FlowPath flowPath) {
425 EventEntry<FlowPath> eventEntry =
426 new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
427 networkEvents.add(eventEntry);
428 }
429
430 /**
431 * Receive a notification that a Flow is removed.
432 *
433 * @param flowPath the flow that is removed.
434 */
435 @Override
436 public void notificationRecvFlowRemoved(FlowPath flowPath) {
437 EventEntry<FlowPath> eventEntry =
438 new EventEntry<FlowPath>(EventEntry.Type.ENTRY_REMOVE, flowPath);
439 networkEvents.add(eventEntry);
440 }
441
442 /**
443 * Receive a notification that a Flow is updated.
444 *
445 * @param flowPath the flow that is updated.
446 */
447 @Override
448 public void notificationRecvFlowUpdated(FlowPath flowPath) {
449 // NOTE: The ADD and UPDATE events are processed in same way
450 EventEntry<FlowPath> eventEntry =
451 new EventEntry<FlowPath>(EventEntry.Type.ENTRY_ADD, flowPath);
452 networkEvents.add(eventEntry);
453 }
454
455 /**
456 * Receive a notification that a Topology Element is added.
457 *
458 * @param topologyElement the Topology Element that is added.
459 */
460 @Override
461 public void notificationRecvTopologyElementAdded(TopologyElement topologyElement) {
462 EventEntry<TopologyElement> eventEntry =
463 new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
464 networkEvents.add(eventEntry);
465 }
466
467 /**
468 * Receive a notification that a Topology Element is removed.
469 *
470 * @param topologyElement the Topology Element that is removed.
471 */
472 @Override
473 public void notificationRecvTopologyElementRemoved(TopologyElement topologyElement) {
474 EventEntry<TopologyElement> eventEntry =
475 new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_REMOVE, topologyElement);
476 networkEvents.add(eventEntry);
477 }
478
479 /**
480 * Receive a notification that a Topology Element is updated.
481 *
482 * @param topologyElement the Topology Element that is updated.
483 */
484 @Override
485 public void notificationRecvTopologyElementUpdated(TopologyElement topologyElement) {
486 // NOTE: The ADD and UPDATE events are processed in same way
487 EventEntry<TopologyElement> eventEntry =
488 new EventEntry<TopologyElement>(EventEntry.Type.ENTRY_ADD, topologyElement);
489 networkEvents.add(eventEntry);
490 }
491}