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