blob: 8dcfd5f866cdb77c942c8269ed1e234245d03511 [file] [log] [blame]
Jonathan Hartaa380972014-04-03 10:24:46 -07001package net.onrc.onos.core.intent.runtime;
Toshio Koide4f308732014-02-18 15:19:48 -08002
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.HashMap;
Toshio Koide798bc1b2014-02-20 14:02:40 -08006import java.util.HashSet;
Toshio Koidedf2eab92014-02-20 11:24:59 -08007import java.util.Iterator;
Toshio Koidebf875662014-02-24 12:19:15 -08008import java.util.LinkedList;
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -07009import java.util.List;
Toshio Koide4f308732014-02-18 15:19:48 -080010import java.util.Map;
Toshio Koide066506e2014-02-20 19:52:09 -080011import java.util.Map.Entry;
TeruUf9111652014-05-14 23:10:35 -070012import java.util.Set;
13import java.util.concurrent.ConcurrentHashMap;
Toshio Koide353a9e12014-06-09 21:03:40 -070014import java.util.concurrent.ConcurrentMap;
Toshio Koide93be5d62014-02-23 19:30:57 -080015import java.util.concurrent.locks.ReentrantLock;
Toshio Koide4f308732014-02-18 15:19:48 -080016
17import net.floodlightcontroller.core.module.FloodlightModuleContext;
18import net.floodlightcontroller.core.module.FloodlightModuleException;
19import net.floodlightcontroller.core.module.IFloodlightModule;
20import net.floodlightcontroller.core.module.IFloodlightService;
Pavlin Radoslavov13669052014-05-13 10:33:39 -070021import net.floodlightcontroller.restserver.IRestApiService;
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -070022import net.floodlightcontroller.util.MACAddress;
23import net.onrc.onos.api.intent.ApplicationIntent;
Jonathan Hart6df90172014-04-03 10:13:11 -070024import net.onrc.onos.core.datagrid.IDatagridService;
25import net.onrc.onos.core.datagrid.IEventChannel;
26import net.onrc.onos.core.datagrid.IEventChannelListener;
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -070027import net.onrc.onos.core.intent.ConstrainedShortestPathIntent;
Jonathan Hartaa380972014-04-03 10:24:46 -070028import net.onrc.onos.core.intent.Intent;
Jonathan Harta99ec672014-04-03 11:30:34 -070029import net.onrc.onos.core.intent.Intent.IntentState;
Jonathan Hartaa380972014-04-03 10:24:46 -070030import net.onrc.onos.core.intent.IntentMap;
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -070031import net.onrc.onos.core.intent.IntentMap.ChangedEvent;
32import net.onrc.onos.core.intent.IntentMap.ChangedListener;
Jonathan Hartaa380972014-04-03 10:24:46 -070033import net.onrc.onos.core.intent.IntentOperation;
Jonathan Harta99ec672014-04-03 11:30:34 -070034import net.onrc.onos.core.intent.IntentOperation.Operator;
Jonathan Hartaa380972014-04-03 10:24:46 -070035import net.onrc.onos.core.intent.IntentOperationList;
36import net.onrc.onos.core.intent.PathIntent;
37import net.onrc.onos.core.intent.PathIntentMap;
38import net.onrc.onos.core.intent.ShortestPathIntent;
Pavlin Radoslavov13669052014-05-13 10:33:39 -070039import net.onrc.onos.core.intent.runtime.web.IntentWebRoutable;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070040import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hart472062d2014-04-03 10:56:48 -070041import net.onrc.onos.core.topology.DeviceEvent;
Jonathan Harte37e4e22014-05-13 19:12:02 -070042import net.onrc.onos.core.topology.ITopologyListener;
43import net.onrc.onos.core.topology.ITopologyService;
Jonathan Hart472062d2014-04-03 10:56:48 -070044import net.onrc.onos.core.topology.LinkEvent;
45import net.onrc.onos.core.topology.PortEvent;
46import net.onrc.onos.core.topology.SwitchEvent;
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -070047import net.onrc.onos.core.util.Dpid;
Toshio Koide4f308732014-02-18 15:19:48 -080048
Jonathan Harta99ec672014-04-03 11:30:34 -070049import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
Toshio Koide0c9106d2014-02-19 15:26:38 -080052/**
53 * @author Toshio Koide (t-koide@onlab.us)
54 */
Jonathan Harte37e4e22014-05-13 19:12:02 -070055public class PathCalcRuntimeModule implements IFloodlightModule, IPathCalcRuntimeService, ITopologyListener, IEventChannelListener<Long, IntentStateList> {
Pavlin Radoslavovfee80982014-04-10 12:12:04 -070056 static class PerfLog {
Ray Milkey269ffb92014-04-03 14:43:30 -070057 private String step;
58 private long time;
Toshio Koidebf875662014-02-24 12:19:15 -080059
Ray Milkey269ffb92014-04-03 14:43:30 -070060 public PerfLog(String step) {
61 this.step = step;
62 this.time = System.nanoTime();
63 }
Toshio Koidebf875662014-02-24 12:19:15 -080064
Ray Milkey269ffb92014-04-03 14:43:30 -070065 public void logThis() {
Pavlin Radoslavov964f8ae2014-04-18 16:44:14 -070066 log.debug("Time:{}, Step:{}", time, step);
Ray Milkey269ffb92014-04-03 14:43:30 -070067 }
68 }
Toshio Koidebf875662014-02-24 12:19:15 -080069
Pavlin Radoslavov53ad5e32014-04-10 14:24:26 -070070 static class PerfLogger {
Ray Milkey269ffb92014-04-03 14:43:30 -070071 private LinkedList<PerfLog> logData = new LinkedList<>();
Toshio Koidebf875662014-02-24 12:19:15 -080072
Ray Milkey269ffb92014-04-03 14:43:30 -070073 public PerfLogger(String logPhase) {
74 log("start_" + logPhase);
75 }
Toshio Koidebf875662014-02-24 12:19:15 -080076
Ray Milkey269ffb92014-04-03 14:43:30 -070077 public void log(String step) {
78 logData.add(new PerfLog(step));
79 }
Toshio Koide27ffd412014-02-18 19:15:27 -080080
Ray Milkey269ffb92014-04-03 14:43:30 -070081 public void flushLog() {
82 log("finish");
Ray Milkey5df613b2014-04-15 10:50:56 -070083 for (PerfLog perfLog : logData) {
84 perfLog.logThis();
Ray Milkey269ffb92014-04-03 14:43:30 -070085 }
86 logData.clear();
87 }
88 }
Toshio Koide4f308732014-02-18 15:19:48 -080089
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -070090 /**
91 * A class to track the deletion of intents and purge them as appropriate.
92 */
93 private class DeleteIntentsTracker implements ChangedListener {
94 @Override
95 public void intentsChange(LinkedList<ChangedEvent> events) {
96 List<String> removeIntentIds = new LinkedList<String>();
97 List<String> removePathIds = new LinkedList<String>();
98
99 //
100 // Process the events one-by-one and collect the Intent IDs of
101 // those intents that should be purged.
102 //
103 for (ChangedEvent event : events) {
104 log.debug("DeleteIntentsTracker: Intent ID {}, eventType {}",
105 event.intent.getId() , event.eventType);
106 PathIntent pathIntent = (PathIntent) pathIntents.getIntent(event.intent.getId());
107 if (pathIntent == null) {
108 continue;
109 }
110
111 //
112 // Test whether the new Intent state allows the Intent
113 // to be purged.
114 //
115 boolean shouldPurge = false;
116 switch (event.eventType) {
117 case ADDED:
118 break;
119 case REMOVED:
120 break;
121 case STATE_CHANGED:
122 IntentState state = pathIntent.getState();
123 switch (state) {
124 case INST_REQ:
125 break;
126 case INST_ACK:
127 break;
128 case INST_NACK:
129 shouldPurge = true;
130 break;
131 case DEL_REQ:
132 break;
133 case DEL_ACK:
134 shouldPurge = true;
135 break;
136 case DEL_PENDING:
137 break;
138 default:
139 break;
140 }
141 break;
142 default:
143 break;
144 }
145
146 if (shouldPurge) {
147 removePathIds.add(pathIntent.getId());
148 Intent parentIntent = pathIntent.getParentIntent();
149 if (parentIntent != null) {
150 //
151 // Remove the High-level Intent only if it was
152 // explicitly deleted by the user via the API.
153 //
154 String intentId = parentIntent.getId();
155 if (removedApplicationIntentIds.contains(intentId)) {
156 removeIntentIds.add(intentId);
157 removedApplicationIntentIds.remove(intentId);
158 }
159 }
160 }
161 }
162
163 // Purge the intents
164 if (!removeIntentIds.isEmpty()) {
165 highLevelIntents.purge(removeIntentIds);
166 }
167 if (!removePathIds.isEmpty()) {
168 pathIntents.purge(removePathIds);
169 }
170 }
171 }
172
Ray Milkey269ffb92014-04-03 14:43:30 -0700173 private PathCalcRuntime runtime;
174 private IDatagridService datagridService;
Jonathan Harte37e4e22014-05-13 19:12:02 -0700175 private ITopologyService topologyService;
Ray Milkey269ffb92014-04-03 14:43:30 -0700176 private IntentMap highLevelIntents;
177 private PathIntentMap pathIntents;
178 private IControllerRegistryService controllerRegistry;
179 private PersistIntent persistIntent;
Pavlin Radoslavov13669052014-05-13 10:33:39 -0700180 private IRestApiService restApi;
Toshio Koide798bc1b2014-02-20 14:02:40 -0800181
Ray Milkey269ffb92014-04-03 14:43:30 -0700182 private IEventChannel<Long, IntentOperationList> opEventChannel;
TeruU9e530662014-05-18 11:49:37 -0700183 private final ReentrantLock lock = new ReentrantLock(true);
Ray Milkey269ffb92014-04-03 14:43:30 -0700184 private static final String INTENT_OP_EVENT_CHANNEL_NAME = "onos.pathintent";
185 private static final String INTENT_STATE_EVENT_CHANNEL_NAME = "onos.pathintent_state";
186 private static final Logger log = LoggerFactory.getLogger(PathCalcRuntimeModule.class);
Toshio Koidea10c0372014-02-20 17:28:10 -0800187
Toshio Koide353a9e12014-06-09 21:03:40 -0700188 private HashSet<LinkEvent> unmatchedLinkEvents = new HashSet<>();
189 private ConcurrentMap<String, Set<Long>> intentInstalledMap = new ConcurrentHashMap<String, Set<Long>>();
190 private ConcurrentMap<String, Intent> staleIntents = new ConcurrentHashMap<String, Intent>();
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700191 private DeleteIntentsTracker deleteIntentsTracker = new DeleteIntentsTracker();
192 private Set<String> removedApplicationIntentIds = new HashSet<String>();
Toshio Koide353a9e12014-06-09 21:03:40 -0700193
Ray Milkey269ffb92014-04-03 14:43:30 -0700194 // ================================================================================
195 // private methods
196 // ================================================================================
197
198 private void reroutePaths(Collection<Intent> oldPaths) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700199 if (oldPaths == null || oldPaths.isEmpty()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700200 return;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700201 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700202
203 IntentOperationList reroutingOperation = new IntentOperationList();
204 for (Intent intent : oldPaths) {
205 PathIntent pathIntent = (PathIntent) intent;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700206 if (pathIntent.isPathFrozen()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700207 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700208 }
Toshio Koide353a9e12014-06-09 21:03:40 -0700209 Intent parentIntent = pathIntent.getParentIntent();
210 if (parentIntent == null) {
211 continue;
212 }
213 if (pathIntent.getState().equals(IntentState.INST_ACK)) {
214 if (!reroutingOperation.contains(parentIntent)) {
215 // reroute now
216 reroutingOperation.add(Operator.ADD, parentIntent);
217 }
218 } else if (pathIntent.getState().equals(IntentState.INST_REQ)) {
219 // reroute after the completion of the current execution
220 staleIntents.put(parentIntent.getId(), parentIntent);
221 log.debug("pending reroute execution for intent ID:{}", parentIntent.getId());
Ray Milkey269ffb92014-04-03 14:43:30 -0700222 }
223 }
224 executeIntentOperations(reroutingOperation);
225 }
Toshio Koidea94060f2014-02-21 22:58:32 -0800226
Toshio Koide27ffd412014-02-18 19:15:27 -0800227
Ray Milkey269ffb92014-04-03 14:43:30 -0700228 // ================================================================================
229 // IFloodlightModule implementations
230 // ================================================================================
Toshio Koide798bc1b2014-02-20 14:02:40 -0800231
Ray Milkey269ffb92014-04-03 14:43:30 -0700232 @Override
233 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
234 Collection<Class<? extends IFloodlightService>> l = new ArrayList<>(1);
235 l.add(IPathCalcRuntimeService.class);
236 return l;
237 }
Toshio Koide4f308732014-02-18 15:19:48 -0800238
Ray Milkey269ffb92014-04-03 14:43:30 -0700239 @Override
240 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
241 Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<>();
242 m.put(IPathCalcRuntimeService.class, this);
243 return m;
244 }
Toshio Koide4f308732014-02-18 15:19:48 -0800245
Ray Milkey269ffb92014-04-03 14:43:30 -0700246 @Override
247 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
248 Collection<Class<? extends IFloodlightService>> l = new ArrayList<>(2);
249 l.add(IDatagridService.class);
Pavlin Radoslavov13669052014-05-13 10:33:39 -0700250 l.add(IRestApiService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700251 l.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700252 return l;
253 }
Toshio Koide4f308732014-02-18 15:19:48 -0800254
Ray Milkey269ffb92014-04-03 14:43:30 -0700255 @Override
256 public void init(FloodlightModuleContext context) throws FloodlightModuleException {
257 datagridService = context.getServiceImpl(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700258 topologyService = context.getServiceImpl(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700259 controllerRegistry = context.getServiceImpl(IControllerRegistryService.class);
Pavlin Radoslavov13669052014-05-13 10:33:39 -0700260 restApi = context.getServiceImpl(IRestApiService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700261 }
Toshio Koide4f308732014-02-18 15:19:48 -0800262
Ray Milkey269ffb92014-04-03 14:43:30 -0700263 @Override
264 public void startUp(FloodlightModuleContext context) {
265 highLevelIntents = new IntentMap();
Jonathan Harte37e4e22014-05-13 19:12:02 -0700266 runtime = new PathCalcRuntime(topologyService.getTopology());
Ray Milkey269ffb92014-04-03 14:43:30 -0700267 pathIntents = new PathIntentMap();
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700268 pathIntents.addChangeListener(deleteIntentsTracker);
Ray Milkey269ffb92014-04-03 14:43:30 -0700269 opEventChannel = datagridService.createChannel(INTENT_OP_EVENT_CHANNEL_NAME, Long.class, IntentOperationList.class);
270 datagridService.addListener(INTENT_STATE_EVENT_CHANNEL_NAME, this, Long.class, IntentStateList.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700271 topologyService.registerTopologyListener(this);
Pavlin Radoslavov0294e052014-04-10 13:36:45 -0700272 persistIntent = new PersistIntent(controllerRegistry);
Pavlin Radoslavov13669052014-05-13 10:33:39 -0700273 restApi.addRestletRoutable(new IntentWebRoutable());
Ray Milkey269ffb92014-04-03 14:43:30 -0700274 }
Toshio Koide27ffd412014-02-18 19:15:27 -0800275
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700276 // ======================================================================
Ray Milkey269ffb92014-04-03 14:43:30 -0700277 // IPathCalcRuntimeService implementations
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700278 // ======================================================================
279
280 /**
281 * Add Application Intents.
282 *
283 * @param appId the Application ID to use.
284 * @param appIntents the Application Intents to add.
285 * @return true on success, otherwise false.
286 */
287 @Override
288 public boolean addApplicationIntents(
289 final String appId,
290 Collection<ApplicationIntent> appIntents) {
291 //
292 // Process all intents one-by-one
293 //
294 // TODO: The Intent Type should be enum instead of a string,
295 // and we should use a switch statement below to process the
296 // different type of intents.
297 //
298 IntentOperationList intentOperations = new IntentOperationList();
299 for (ApplicationIntent appIntent : appIntents) {
300 String appIntentId = appId + ":" + appIntent.intentId();
301
302 IntentOperation.Operator operator = IntentOperation.Operator.ADD;
303 Dpid srcSwitchDpid = new Dpid(appIntent.srcSwitchDpid());
304 Dpid dstSwitchDpid = new Dpid(appIntent.dstSwitchDpid());
305
306 if (appIntent.intentType().equals("SHORTEST_PATH")) {
307 //
308 // Process Shortest-Path Intent
309 //
310 ShortestPathIntent spi =
311 new ShortestPathIntent(appIntentId,
312 srcSwitchDpid.value(),
313 appIntent.srcSwitchPort(),
314 MACAddress.valueOf(appIntent.matchSrcMac()).toLong(),
315 dstSwitchDpid.value(),
316 appIntent.dstSwitchPort(),
317 MACAddress.valueOf(appIntent.matchDstMac()).toLong());
318 spi.setPathFrozen(appIntent.isStaticPath());
319 intentOperations.add(operator, spi);
320 } else if (appIntent.intentType().equals("CONSTRAINED_SHORTEST_PATH")) {
321 //
322 // Process Constrained Shortest-Path Intent
323 //
324 ConstrainedShortestPathIntent cspi =
325 new ConstrainedShortestPathIntent(appIntentId,
326 srcSwitchDpid.value(),
327 appIntent.srcSwitchPort(),
328 MACAddress.valueOf(appIntent.matchSrcMac()).toLong(),
329 dstSwitchDpid.value(),
330 appIntent.dstSwitchPort(),
331 MACAddress.valueOf(appIntent.matchDstMac()).toLong(),
332 appIntent.bandwidth());
333 cspi.setPathFrozen(appIntent.isStaticPath());
334 intentOperations.add(operator, cspi);
335 } else {
336 log.error("Unknown Application Intent Type: {}",
337 appIntent.intentType());
338 return false;
339 }
340 removedApplicationIntentIds.remove(appIntentId);
341 }
342 // Apply the Intent Operations
343 executeIntentOperations(intentOperations);
344 return true;
345 }
346
347 /**
348 * Remove Application Intents.
349 *
350 * @param appId the Application ID to use.
351 * @param intentIds the Application Intent IDs to remove.
352 * @return true on success, otherwise false.
353 */
354 @Override
355 public boolean removeApplicationIntents(final String appId,
356 Collection<String> intentIds) {
357 IntentMap intentMap = getHighLevelIntents();
358
359 //
360 // Process all intents one-by-one
361 //
362 IntentOperationList operations = new IntentOperationList();
363 for (String intentId : intentIds) {
364 String appIntentId = appId + ":" + intentId;
365 Intent intent = intentMap.getIntent(appIntentId);
366 if (intent != null) {
367 operations.add(IntentOperation.Operator.REMOVE, intent);
368 removedApplicationIntentIds.add(appIntentId);
369 }
370 }
371 executeIntentOperations(operations);
372
373 return true;
374 }
375
376 /**
377 * Remove all Application Intents.
378 *
379 * @param appId the Application ID to use.
380 * @return true on success, otherwise false.
381 */
382 @Override
383 public boolean removeAllApplicationIntents(final String appId) {
384 IntentMap intentMap = getHighLevelIntents();
385
386 //
387 // Remove all intents
388 //
389 IntentOperationList operations = new IntentOperationList();
390 for (Intent intent : intentMap.getAllIntents()) {
391 operations.add(IntentOperation.Operator.REMOVE, intent);
392 removedApplicationIntentIds.add(intent.getId());
393 }
394 executeIntentOperations(operations);
395
396 return true;
397 }
Toshio Koide798bc1b2014-02-20 14:02:40 -0800398
Ray Milkey269ffb92014-04-03 14:43:30 -0700399 @Override
400 public IntentOperationList executeIntentOperations(IntentOperationList list) {
TeruU9e530662014-05-18 11:49:37 -0700401
Ray Milkeyb29e6262014-04-09 16:02:14 -0700402 if (list == null || list.size() == 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700403 return null;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700404 }
Toshio Koide275d8142014-02-24 16:41:52 -0800405
Ray Milkey269ffb92014-04-03 14:43:30 -0700406 lock.lock(); // TODO optimize locking using smaller steps
407 try {
TeruU9e530662014-05-18 11:49:37 -0700408 log.trace("lock executeIntentOperations, lock obj is already locked? {}", lock.isLocked());
Ray Milkey269ffb92014-04-03 14:43:30 -0700409 // update the map of high-level intents
TeruU9e530662014-05-18 11:49:37 -0700410
Ray Milkey269ffb92014-04-03 14:43:30 -0700411 highLevelIntents.executeOperations(list);
Toshio Koidedf2eab92014-02-20 11:24:59 -0800412
Ray Milkey269ffb92014-04-03 14:43:30 -0700413 // change states of high-level intents
414 IntentStateList states = new IntentStateList();
415 for (IntentOperation op : list) {
416 switch (op.operator) {
417 case ADD:
418 switch (op.intent.getState()) {
419 case CREATED:
420 states.put(op.intent.getId(), IntentState.INST_REQ);
421 break;
422 case INST_ACK:
423 states.put(op.intent.getId(), IntentState.REROUTE_REQ);
424 break;
425 default:
426 break;
427 }
428 break;
429 case REMOVE:
430 switch (op.intent.getState()) {
431 case CREATED:
432 states.put(op.intent.getId(), IntentState.DEL_REQ);
433 break;
434 default:
435 break;
436 }
437 break;
438 default:
439 break;
440 }
441 }
442 highLevelIntents.changeStates(states);
Toshio Koidedf2eab92014-02-20 11:24:59 -0800443
Ray Milkey269ffb92014-04-03 14:43:30 -0700444 // calculate path-intents (low-level operations)
Ray Milkey269ffb92014-04-03 14:43:30 -0700445 IntentOperationList pathIntentOperations = runtime.calcPathIntents(list, highLevelIntents, pathIntents);
Toshio Koide600ae5f2014-02-20 18:42:00 -0800446
Ray Milkey269ffb92014-04-03 14:43:30 -0700447 // persist calculated low-level operations into data store
Ray Milkey269ffb92014-04-03 14:43:30 -0700448 long key = persistIntent.getKey();
449 persistIntent.persistIfLeader(key, pathIntentOperations);
Toshio Koide93be5d62014-02-23 19:30:57 -0800450
Ray Milkey269ffb92014-04-03 14:43:30 -0700451 // remove error-intents and reflect them to high-level intents
Ray Milkey269ffb92014-04-03 14:43:30 -0700452 states.clear();
453 Iterator<IntentOperation> i = pathIntentOperations.iterator();
454 while (i.hasNext()) {
455 IntentOperation op = i.next();
456 if (op.operator.equals(Operator.ERROR)) {
457 states.put(op.intent.getId(), IntentState.INST_NACK);
458 i.remove();
459 }
460 }
461 highLevelIntents.changeStates(states);
Toshio Koidea94060f2014-02-21 22:58:32 -0800462
Ray Milkey269ffb92014-04-03 14:43:30 -0700463 // update the map of path intents and publish the path operations
Ray Milkey269ffb92014-04-03 14:43:30 -0700464 pathIntents.executeOperations(pathIntentOperations);
Toshio Koide93be5d62014-02-23 19:30:57 -0800465
Ray Milkey269ffb92014-04-03 14:43:30 -0700466 // send notification
Ray Milkey269ffb92014-04-03 14:43:30 -0700467 // XXX: Send notifications using the same key every time
468 // and receive them by entryAdded() and entryUpdated()
469 opEventChannel.addEntry(0L, pathIntentOperations);
Ray Milkey269ffb92014-04-03 14:43:30 -0700470 //opEventChannel.removeEntry(key);
471 return pathIntentOperations;
472 } finally {
Ray Milkey269ffb92014-04-03 14:43:30 -0700473 lock.unlock();
TeruU9e530662014-05-18 11:49:37 -0700474 log.trace("unlock executeIntentOperations");
Ray Milkey269ffb92014-04-03 14:43:30 -0700475 }
476 }
Toshio Koide27ffd412014-02-18 19:15:27 -0800477
Ray Milkey269ffb92014-04-03 14:43:30 -0700478 @Override
479 public IntentMap getHighLevelIntents() {
480 return highLevelIntents;
481 }
Toshio Koide27ffd412014-02-18 19:15:27 -0800482
Ray Milkey269ffb92014-04-03 14:43:30 -0700483 @Override
484 public IntentMap getPathIntents() {
485 return pathIntents;
486 }
Toshio Koide27ffd412014-02-18 19:15:27 -0800487
Ray Milkey269ffb92014-04-03 14:43:30 -0700488 @Override
489 public void purgeIntents() {
490 highLevelIntents.purge();
491 pathIntents.purge();
492 }
Toshio Koide0c9106d2014-02-19 15:26:38 -0800493
Ray Milkey269ffb92014-04-03 14:43:30 -0700494 // ================================================================================
Jonathan Harte37e4e22014-05-13 19:12:02 -0700495 // ITopologyListener implementations
Ray Milkey269ffb92014-04-03 14:43:30 -0700496 // ================================================================================
Toshio Koide798bc1b2014-02-20 14:02:40 -0800497
Ray Milkeya5450cc2014-04-17 14:31:30 -0700498 // CHECKSTYLE:OFF suppress warning about too many parameters
Ray Milkey269ffb92014-04-03 14:43:30 -0700499 @Override
Jonathan Harte37e4e22014-05-13 19:12:02 -0700500 public void topologyEvents(Collection<SwitchEvent> addedSwitchEvents,
Ray Milkey269ffb92014-04-03 14:43:30 -0700501 Collection<SwitchEvent> removedSwitchEvents,
502 Collection<PortEvent> addedPortEvents,
503 Collection<PortEvent> removedPortEvents,
504 Collection<LinkEvent> addedLinkEvents,
505 Collection<LinkEvent> removedLinkEvents,
506 Collection<DeviceEvent> addedDeviceEvents,
507 Collection<DeviceEvent> removedDeviceEvents) {
Ray Milkeya5450cc2014-04-17 14:31:30 -0700508 // CHECKSTYLE:ON
Toshio Koidea9078af2014-02-21 16:57:04 -0800509
Ray Milkey269ffb92014-04-03 14:43:30 -0700510 PerfLogger p = new PerfLogger("networkGraphEvents");
511 HashSet<Intent> affectedPaths = new HashSet<>();
Toshio Koide93797dc2014-02-27 23:54:26 -0800512
Ray Milkey269ffb92014-04-03 14:43:30 -0700513 boolean rerouteAll = false;
514 for (LinkEvent le : addedLinkEvents) {
515 LinkEvent rev = new LinkEvent(le.getDst().getDpid(), le.getDst().getNumber(), le.getSrc().getDpid(), le.getSrc().getNumber());
516 if (unmatchedLinkEvents.contains(rev)) {
517 rerouteAll = true;
518 unmatchedLinkEvents.remove(rev);
519 log.debug("Found matched LinkEvent: {} {}", rev, le);
520 } else {
521 unmatchedLinkEvents.add(le);
522 log.debug("Adding unmatched LinkEvent: {}", le);
523 }
524 }
525 for (LinkEvent le : removedLinkEvents) {
526 if (unmatchedLinkEvents.contains(le)) {
527 unmatchedLinkEvents.remove(le);
528 log.debug("Removing LinkEvent: {}", le);
529 }
530 }
531 if (unmatchedLinkEvents.size() > 0) {
532 log.debug("Unmatched link events: {} events", unmatchedLinkEvents.size());
533 }
Toshio Koidea9078af2014-02-21 16:57:04 -0800534
Ray Milkey7f1567c2014-04-08 13:53:32 -0700535 if (rerouteAll) { //addedLinkEvents.size() > 0) { // ||
Ray Milkey269ffb92014-04-03 14:43:30 -0700536// addedPortEvents.size() > 0 ||
537// addedSwitchEvents.size() > 0) {
538 p.log("begin_getAllIntents");
539 affectedPaths.addAll(getPathIntents().getAllIntents());
540 p.log("end_getAllIntents");
541 } else if (removedSwitchEvents.size() > 0 ||
542 removedLinkEvents.size() > 0 ||
543 removedPortEvents.size() > 0) {
544 p.log("begin_getIntentsByLink");
Ray Milkeyb29e6262014-04-09 16:02:14 -0700545 for (LinkEvent linkEvent : removedLinkEvents) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700546 affectedPaths.addAll(pathIntents.getIntentsByLink(linkEvent));
Ray Milkeyb29e6262014-04-09 16:02:14 -0700547 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700548 p.log("end_getIntentsByLink");
Toshio Koidea9078af2014-02-21 16:57:04 -0800549
Ray Milkey269ffb92014-04-03 14:43:30 -0700550 p.log("begin_getIntentsByPort");
Ray Milkeyb29e6262014-04-09 16:02:14 -0700551 for (PortEvent portEvent : removedPortEvents) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700552 affectedPaths.addAll(pathIntents.getIntentsByPort(portEvent.getDpid(), portEvent.getNumber()));
Ray Milkeyb29e6262014-04-09 16:02:14 -0700553 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700554 p.log("end_getIntentsByPort");
Toshio Koidea9078af2014-02-21 16:57:04 -0800555
Ray Milkey269ffb92014-04-03 14:43:30 -0700556 p.log("begin_getIntentsByDpid");
Ray Milkeyb29e6262014-04-09 16:02:14 -0700557 for (SwitchEvent switchEvent : removedSwitchEvents) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700558 affectedPaths.addAll(pathIntents.getIntentsByDpid(switchEvent.getDpid()));
Ray Milkeyb29e6262014-04-09 16:02:14 -0700559 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700560 p.log("end_getIntentsByDpid");
561 }
562 p.log("begin_reroutePaths");
563 reroutePaths(affectedPaths);
564 p.log("end_reroutePaths");
565 p.flushLog();
566 }
Toshio Koide066506e2014-02-20 19:52:09 -0800567
Ray Milkey269ffb92014-04-03 14:43:30 -0700568 // ================================================================================
569 // IEventChannelListener implementations
570 // ================================================================================
Toshio Koide066506e2014-02-20 19:52:09 -0800571
Ray Milkey269ffb92014-04-03 14:43:30 -0700572 @Override
573 public void entryAdded(IntentStateList value) {
574 entryUpdated(value);
575 }
Toshio Koide066506e2014-02-20 19:52:09 -0800576
Ray Milkey269ffb92014-04-03 14:43:30 -0700577 @Override
578 public void entryRemoved(IntentStateList value) {
579 // do nothing
580 }
Toshio Koide066506e2014-02-20 19:52:09 -0800581
Ray Milkey149693c2014-05-20 14:58:53 -0700582 @SuppressWarnings("fallthrough")
Ray Milkey269ffb92014-04-03 14:43:30 -0700583 @Override
584 public void entryUpdated(IntentStateList value) {
585 // TODO draw state transition diagram in multiple ONOS instances and update this method
TeruU9e530662014-05-18 11:49:37 -0700586
Toshio Koide353a9e12014-06-09 21:03:40 -0700587 IntentOperationList opList = new IntentOperationList();
Ray Milkey269ffb92014-04-03 14:43:30 -0700588 lock.lock(); // TODO optimize locking using smaller steps
589 try {
TeruU9e530662014-05-18 11:49:37 -0700590 log.trace("lock entryUpdated, lock obj is already locked? {}", lock.isLocked());
Ray Milkey269ffb92014-04-03 14:43:30 -0700591 // reflect state changes of path-level intent into application-level intents
Ray Milkey269ffb92014-04-03 14:43:30 -0700592 IntentStateList highLevelIntentStates = new IntentStateList();
593 IntentStateList pathIntentStates = new IntentStateList();
594 for (Entry<String, IntentState> entry : value.entrySet()) {
Toshio Koide14dba902014-06-05 18:39:21 -0700595 String pathIntentId = entry.getKey();
596 IntentState nextPathIntentState = entry.getValue();
597 PathIntent pathIntent = (PathIntent) pathIntents.getIntent(pathIntentId);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700598 if (pathIntent == null) {
599 continue;
600 }
Toshio Koide8315d7d2014-02-21 22:58:32 -0800601
Ray Milkey269ffb92014-04-03 14:43:30 -0700602 Intent parentIntent = pathIntent.getParentIntent();
603 if (parentIntent == null ||
Toshio Koide14dba902014-06-05 18:39:21 -0700604 !(parentIntent instanceof ShortestPathIntent)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700605 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700606 }
Toshio Koide066506e2014-02-20 19:52:09 -0800607
Toshio Koide14dba902014-06-05 18:39:21 -0700608 boolean isChildIntent = ((ShortestPathIntent) parentIntent).getPathIntentId().equals(pathIntentId);
Toshio Koide353a9e12014-06-09 21:03:40 -0700609
610 // Check necessity for retrying the intent execution.
611 // When the PathIntent(=isChildIntent) transitioned to INST_{ACK/NACK}
612 // but was marked as stale (e.g., has been requested to reroute by Topology event),
613 // then immediately enqueue the re-computation of parent intent.
614 if (isChildIntent && staleIntents.containsKey(parentIntent.getId()) && (
615 nextPathIntentState.equals(IntentState.INST_ACK) ||
616 nextPathIntentState.equals(IntentState.INST_NACK))) {
617 opList.add(Operator.ADD, parentIntent);
618 staleIntents.remove(parentIntent.getId());
619 log.debug("retrying intent execution for intent ID:{}", parentIntent.getId());
620 }
621
Toshio Koide14dba902014-06-05 18:39:21 -0700622 switch (nextPathIntentState) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700623 case INST_ACK:
TeruUf9111652014-05-14 23:10:35 -0700624 Set<Long> installedDpids = calcInstalledDpids(pathIntent, value.domainSwitchDpids);
625 if (!isFlowInstalled(pathIntent, installedDpids)) {
626 break;
627 }
628 // FALLTHROUGH
Ray Milkey269ffb92014-04-03 14:43:30 -0700629 case INST_NACK:
TeruUf9111652014-05-14 23:10:35 -0700630 // FALLTHROUGH
Ray Milkey269ffb92014-04-03 14:43:30 -0700631 case DEL_PENDING:
Toshio Koide14dba902014-06-05 18:39:21 -0700632 if (isChildIntent) {
633 log.debug("put the state highLevelIntentStates ID {}, state {}", parentIntent.getId(), nextPathIntentState);
634 highLevelIntentStates.put(parentIntent.getId(), nextPathIntentState);
635 }
636 log.debug("put the state pathIntentStates ID {}, state {}", pathIntentId, nextPathIntentState);
637 pathIntentStates.put(pathIntentId, nextPathIntentState);
TeruU9e530662014-05-18 11:49:37 -0700638 break;
639 case DEL_ACK:
Toshio Koide14dba902014-06-05 18:39:21 -0700640 if (isChildIntent) {
641 if (intentInstalledMap.containsKey(parentIntent.getId())) {
642 intentInstalledMap.remove(parentIntent.getId());
643 }
644 log.debug("put the state highLevelIntentStates ID {}, state {}", parentIntent.getId(), nextPathIntentState);
645 highLevelIntentStates.put(parentIntent.getId(), nextPathIntentState);
TeruU9e530662014-05-18 11:49:37 -0700646 }
Toshio Koide14dba902014-06-05 18:39:21 -0700647 log.debug("put the state pathIntentStates ID {}, state {}", pathIntentId, nextPathIntentState);
648 pathIntentStates.put(pathIntentId, nextPathIntentState);
649 break;
650 case CREATED:
651 break;
652 case DEL_REQ:
653 break;
654 case INST_REQ:
655 break;
656 case REROUTE_REQ:
Ray Milkey269ffb92014-04-03 14:43:30 -0700657 break;
658 default:
659 break;
660 }
661 }
662 highLevelIntents.changeStates(highLevelIntentStates);
663 pathIntents.changeStates(pathIntentStates);
Ray Milkey269ffb92014-04-03 14:43:30 -0700664 } finally {
Ray Milkey269ffb92014-04-03 14:43:30 -0700665 lock.unlock();
TeruU9e530662014-05-18 11:49:37 -0700666 log.trace("unlock entryUpdated");
Ray Milkey269ffb92014-04-03 14:43:30 -0700667 }
Toshio Koide353a9e12014-06-09 21:03:40 -0700668 executeIntentOperations(opList);
Ray Milkey269ffb92014-04-03 14:43:30 -0700669 }
TeruUf9111652014-05-14 23:10:35 -0700670
671 /***
672 * This function is to check whether the entire path's flow entries are installed or not.
TeruU9e530662014-05-18 11:49:37 -0700673 *
TeruUf9111652014-05-14 23:10:35 -0700674 * @param pathIntent : The pathIntent to be checked
675 * @param installedDpids : The dpids installed on one ONOS instance
676 * @return The result of whether a pathIntent has been installed or not.
677 */
678 private boolean isFlowInstalled(PathIntent pathIntent, Set<Long> installedDpids) {
679 String parentIntentId = pathIntent.getParentIntent().getId();
680 log.debug("parentIntentId {}", parentIntentId);
681
682 if (intentInstalledMap.containsKey(parentIntentId)) {
683 if (!installedDpids.isEmpty()) {
684 intentInstalledMap.get(parentIntentId).addAll(installedDpids);
685 }
686 } else {
687 // This is the creation of an entry.
688 intentInstalledMap.put(parentIntentId, installedDpids);
689 }
690
691 Set<Long> allSwitchesForPath = new HashSet<Long>();
692 ShortestPathIntent spfIntent = (ShortestPathIntent) pathIntent.getParentIntent();
693
694 for (LinkEvent linkEvent : pathIntent.getPath()) {
695 long sw = linkEvent.getSrc().getDpid();
696 allSwitchesForPath.add(sw);
697 }
698 allSwitchesForPath.add(spfIntent.getDstSwitchDpid());
699
700 if (log.isTraceEnabled()) {
701 log.trace("All switches {}, installed installedDpids {}", allSwitchesForPath, intentInstalledMap.get(parentIntentId));
702 }
703
704 if (allSwitchesForPath.equals(intentInstalledMap.get(parentIntentId))) {
705 intentInstalledMap.remove(parentIntentId);
706 return true;
707 }
708
709 return false;
710 }
711
712 private Set<Long> calcInstalledDpids(PathIntent pathIntent, Set<Long> domainSwitchDpids) {
713 Set<Long> allSwitchesForPath = new HashSet<Long>();
714 ShortestPathIntent spfIntent = (ShortestPathIntent) pathIntent.getParentIntent();
715
716 for (LinkEvent linkEvent : pathIntent.getPath()) {
717 long sw = linkEvent.getSrc().getDpid();
718
719 if (domainSwitchDpids.contains(sw)) {
720 allSwitchesForPath.add(sw);
721 }
722 }
723
724 if (domainSwitchDpids.contains(spfIntent.getDstSwitchDpid())) {
725 allSwitchesForPath.add(spfIntent.getDstSwitchDpid());
726 }
727
728 if (log.isTraceEnabled()) {
TeruU9e530662014-05-18 11:49:37 -0700729 log.trace("All switches for a path {}, domain switch dpids {}", allSwitchesForPath, domainSwitchDpids);
TeruUf9111652014-05-14 23:10:35 -0700730 }
731
732 return allSwitchesForPath;
733 }
Toshio Koidea9078af2014-02-21 16:57:04 -0800734}