blob: 2f820a04079db2c49d1d458085b1b2baa8643f2d [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 Hartc00f5c22014-06-10 15:14:40 -070055public class PathCalcRuntimeModule implements IFloodlightModule,
56 IPathCalcRuntimeService,
57 ITopologyListener,
58 IEventChannelListener<Long, IntentStateList> {
Pavlin Radoslavovfee80982014-04-10 12:12:04 -070059 static class PerfLog {
Ray Milkey269ffb92014-04-03 14:43:30 -070060 private String step;
61 private long time;
Toshio Koidebf875662014-02-24 12:19:15 -080062
Ray Milkey269ffb92014-04-03 14:43:30 -070063 public PerfLog(String step) {
64 this.step = step;
65 this.time = System.nanoTime();
66 }
Toshio Koidebf875662014-02-24 12:19:15 -080067
Ray Milkey269ffb92014-04-03 14:43:30 -070068 public void logThis() {
Pavlin Radoslavov964f8ae2014-04-18 16:44:14 -070069 log.debug("Time:{}, Step:{}", time, step);
Ray Milkey269ffb92014-04-03 14:43:30 -070070 }
71 }
Toshio Koidebf875662014-02-24 12:19:15 -080072
Pavlin Radoslavov53ad5e32014-04-10 14:24:26 -070073 static class PerfLogger {
Ray Milkey269ffb92014-04-03 14:43:30 -070074 private LinkedList<PerfLog> logData = new LinkedList<>();
Toshio Koidebf875662014-02-24 12:19:15 -080075
Ray Milkey269ffb92014-04-03 14:43:30 -070076 public PerfLogger(String logPhase) {
77 log("start_" + logPhase);
78 }
Toshio Koidebf875662014-02-24 12:19:15 -080079
Ray Milkey269ffb92014-04-03 14:43:30 -070080 public void log(String step) {
81 logData.add(new PerfLog(step));
82 }
Toshio Koide27ffd412014-02-18 19:15:27 -080083
Ray Milkey269ffb92014-04-03 14:43:30 -070084 public void flushLog() {
85 log("finish");
Ray Milkey5df613b2014-04-15 10:50:56 -070086 for (PerfLog perfLog : logData) {
87 perfLog.logThis();
Ray Milkey269ffb92014-04-03 14:43:30 -070088 }
89 logData.clear();
90 }
91 }
Toshio Koide4f308732014-02-18 15:19:48 -080092
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -070093 /**
94 * A class to track the deletion of intents and purge them as appropriate.
95 */
96 private class DeleteIntentsTracker implements ChangedListener {
97 @Override
98 public void intentsChange(LinkedList<ChangedEvent> events) {
99 List<String> removeIntentIds = new LinkedList<String>();
100 List<String> removePathIds = new LinkedList<String>();
101
102 //
103 // Process the events one-by-one and collect the Intent IDs of
104 // those intents that should be purged.
105 //
106 for (ChangedEvent event : events) {
107 log.debug("DeleteIntentsTracker: Intent ID {}, eventType {}",
108 event.intent.getId() , event.eventType);
109 PathIntent pathIntent = (PathIntent) pathIntents.getIntent(event.intent.getId());
110 if (pathIntent == null) {
111 continue;
112 }
113
114 //
115 // Test whether the new Intent state allows the Intent
116 // to be purged.
117 //
118 boolean shouldPurge = false;
119 switch (event.eventType) {
120 case ADDED:
121 break;
122 case REMOVED:
123 break;
124 case STATE_CHANGED:
125 IntentState state = pathIntent.getState();
126 switch (state) {
127 case INST_REQ:
128 break;
129 case INST_ACK:
130 break;
131 case INST_NACK:
132 shouldPurge = true;
133 break;
134 case DEL_REQ:
135 break;
136 case DEL_ACK:
137 shouldPurge = true;
138 break;
139 case DEL_PENDING:
140 break;
141 default:
142 break;
143 }
144 break;
145 default:
146 break;
147 }
148
149 if (shouldPurge) {
150 removePathIds.add(pathIntent.getId());
151 Intent parentIntent = pathIntent.getParentIntent();
152 if (parentIntent != null) {
153 //
154 // Remove the High-level Intent only if it was
155 // explicitly deleted by the user via the API.
156 //
157 String intentId = parentIntent.getId();
158 if (removedApplicationIntentIds.contains(intentId)) {
159 removeIntentIds.add(intentId);
160 removedApplicationIntentIds.remove(intentId);
161 }
162 }
163 }
164 }
165
166 // Purge the intents
167 if (!removeIntentIds.isEmpty()) {
168 highLevelIntents.purge(removeIntentIds);
169 }
170 if (!removePathIds.isEmpty()) {
171 pathIntents.purge(removePathIds);
172 }
173 }
174 }
175
Ray Milkey269ffb92014-04-03 14:43:30 -0700176 private PathCalcRuntime runtime;
177 private IDatagridService datagridService;
Jonathan Harte37e4e22014-05-13 19:12:02 -0700178 private ITopologyService topologyService;
Ray Milkey269ffb92014-04-03 14:43:30 -0700179 private IntentMap highLevelIntents;
180 private PathIntentMap pathIntents;
181 private IControllerRegistryService controllerRegistry;
182 private PersistIntent persistIntent;
Pavlin Radoslavov13669052014-05-13 10:33:39 -0700183 private IRestApiService restApi;
Toshio Koide798bc1b2014-02-20 14:02:40 -0800184
Ray Milkey269ffb92014-04-03 14:43:30 -0700185 private IEventChannel<Long, IntentOperationList> opEventChannel;
TeruU9e530662014-05-18 11:49:37 -0700186 private final ReentrantLock lock = new ReentrantLock(true);
Ray Milkey269ffb92014-04-03 14:43:30 -0700187 private static final String INTENT_OP_EVENT_CHANNEL_NAME = "onos.pathintent";
188 private static final String INTENT_STATE_EVENT_CHANNEL_NAME = "onos.pathintent_state";
189 private static final Logger log = LoggerFactory.getLogger(PathCalcRuntimeModule.class);
Toshio Koidea10c0372014-02-20 17:28:10 -0800190
Toshio Koide353a9e12014-06-09 21:03:40 -0700191 private HashSet<LinkEvent> unmatchedLinkEvents = new HashSet<>();
192 private ConcurrentMap<String, Set<Long>> intentInstalledMap = new ConcurrentHashMap<String, Set<Long>>();
193 private ConcurrentMap<String, Intent> staleIntents = new ConcurrentHashMap<String, Intent>();
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700194 private DeleteIntentsTracker deleteIntentsTracker = new DeleteIntentsTracker();
195 private Set<String> removedApplicationIntentIds = new HashSet<String>();
Toshio Koide353a9e12014-06-09 21:03:40 -0700196
Ray Milkey269ffb92014-04-03 14:43:30 -0700197 // ================================================================================
198 // private methods
199 // ================================================================================
200
201 private void reroutePaths(Collection<Intent> oldPaths) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700202 if (oldPaths == null || oldPaths.isEmpty()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700203 return;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700204 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700205
206 IntentOperationList reroutingOperation = new IntentOperationList();
207 for (Intent intent : oldPaths) {
208 PathIntent pathIntent = (PathIntent) intent;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700209 if (pathIntent.isPathFrozen()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700210 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700211 }
Toshio Koide353a9e12014-06-09 21:03:40 -0700212 Intent parentIntent = pathIntent.getParentIntent();
213 if (parentIntent == null) {
214 continue;
215 }
216 if (pathIntent.getState().equals(IntentState.INST_ACK)) {
217 if (!reroutingOperation.contains(parentIntent)) {
218 // reroute now
219 reroutingOperation.add(Operator.ADD, parentIntent);
220 }
221 } else if (pathIntent.getState().equals(IntentState.INST_REQ)) {
222 // reroute after the completion of the current execution
223 staleIntents.put(parentIntent.getId(), parentIntent);
224 log.debug("pending reroute execution for intent ID:{}", parentIntent.getId());
Ray Milkey269ffb92014-04-03 14:43:30 -0700225 }
226 }
227 executeIntentOperations(reroutingOperation);
228 }
Toshio Koidea94060f2014-02-21 22:58:32 -0800229
Toshio Koide27ffd412014-02-18 19:15:27 -0800230
Ray Milkey269ffb92014-04-03 14:43:30 -0700231 // ================================================================================
232 // IFloodlightModule implementations
233 // ================================================================================
Toshio Koide798bc1b2014-02-20 14:02:40 -0800234
Ray Milkey269ffb92014-04-03 14:43:30 -0700235 @Override
236 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
237 Collection<Class<? extends IFloodlightService>> l = new ArrayList<>(1);
238 l.add(IPathCalcRuntimeService.class);
239 return l;
240 }
Toshio Koide4f308732014-02-18 15:19:48 -0800241
Ray Milkey269ffb92014-04-03 14:43:30 -0700242 @Override
243 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
244 Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<>();
245 m.put(IPathCalcRuntimeService.class, this);
246 return m;
247 }
Toshio Koide4f308732014-02-18 15:19:48 -0800248
Ray Milkey269ffb92014-04-03 14:43:30 -0700249 @Override
250 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
251 Collection<Class<? extends IFloodlightService>> l = new ArrayList<>(2);
252 l.add(IDatagridService.class);
Pavlin Radoslavov13669052014-05-13 10:33:39 -0700253 l.add(IRestApiService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700254 l.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700255 return l;
256 }
Toshio Koide4f308732014-02-18 15:19:48 -0800257
Ray Milkey269ffb92014-04-03 14:43:30 -0700258 @Override
259 public void init(FloodlightModuleContext context) throws FloodlightModuleException {
260 datagridService = context.getServiceImpl(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700261 topologyService = context.getServiceImpl(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700262 controllerRegistry = context.getServiceImpl(IControllerRegistryService.class);
Pavlin Radoslavov13669052014-05-13 10:33:39 -0700263 restApi = context.getServiceImpl(IRestApiService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700264 }
Toshio Koide4f308732014-02-18 15:19:48 -0800265
Ray Milkey269ffb92014-04-03 14:43:30 -0700266 @Override
267 public void startUp(FloodlightModuleContext context) {
268 highLevelIntents = new IntentMap();
Jonathan Harte37e4e22014-05-13 19:12:02 -0700269 runtime = new PathCalcRuntime(topologyService.getTopology());
Ray Milkey269ffb92014-04-03 14:43:30 -0700270 pathIntents = new PathIntentMap();
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700271 pathIntents.addChangeListener(deleteIntentsTracker);
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700272 opEventChannel = datagridService.createChannel(
273 INTENT_OP_EVENT_CHANNEL_NAME, Long.class, IntentOperationList.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700274 datagridService.addListener(INTENT_STATE_EVENT_CHANNEL_NAME, this, Long.class, IntentStateList.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700275 topologyService.registerTopologyListener(this);
Pavlin Radoslavov0294e052014-04-10 13:36:45 -0700276 persistIntent = new PersistIntent(controllerRegistry);
Pavlin Radoslavov13669052014-05-13 10:33:39 -0700277 restApi.addRestletRoutable(new IntentWebRoutable());
Ray Milkey269ffb92014-04-03 14:43:30 -0700278 }
Toshio Koide27ffd412014-02-18 19:15:27 -0800279
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700280 // ======================================================================
Ray Milkey269ffb92014-04-03 14:43:30 -0700281 // IPathCalcRuntimeService implementations
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700282 // ======================================================================
283
284 /**
285 * Add Application Intents.
286 *
287 * @param appId the Application ID to use.
288 * @param appIntents the Application Intents to add.
289 * @return true on success, otherwise false.
290 */
291 @Override
292 public boolean addApplicationIntents(
293 final String appId,
294 Collection<ApplicationIntent> appIntents) {
295 //
296 // Process all intents one-by-one
297 //
298 // TODO: The Intent Type should be enum instead of a string,
299 // and we should use a switch statement below to process the
300 // different type of intents.
301 //
302 IntentOperationList intentOperations = new IntentOperationList();
303 for (ApplicationIntent appIntent : appIntents) {
304 String appIntentId = appId + ":" + appIntent.intentId();
305
306 IntentOperation.Operator operator = IntentOperation.Operator.ADD;
307 Dpid srcSwitchDpid = new Dpid(appIntent.srcSwitchDpid());
308 Dpid dstSwitchDpid = new Dpid(appIntent.dstSwitchDpid());
309
310 if (appIntent.intentType().equals("SHORTEST_PATH")) {
311 //
312 // Process Shortest-Path Intent
313 //
314 ShortestPathIntent spi =
315 new ShortestPathIntent(appIntentId,
316 srcSwitchDpid.value(),
317 appIntent.srcSwitchPort(),
318 MACAddress.valueOf(appIntent.matchSrcMac()).toLong(),
319 dstSwitchDpid.value(),
320 appIntent.dstSwitchPort(),
321 MACAddress.valueOf(appIntent.matchDstMac()).toLong());
322 spi.setPathFrozen(appIntent.isStaticPath());
323 intentOperations.add(operator, spi);
324 } else if (appIntent.intentType().equals("CONSTRAINED_SHORTEST_PATH")) {
325 //
326 // Process Constrained Shortest-Path Intent
327 //
328 ConstrainedShortestPathIntent cspi =
329 new ConstrainedShortestPathIntent(appIntentId,
330 srcSwitchDpid.value(),
331 appIntent.srcSwitchPort(),
332 MACAddress.valueOf(appIntent.matchSrcMac()).toLong(),
333 dstSwitchDpid.value(),
334 appIntent.dstSwitchPort(),
335 MACAddress.valueOf(appIntent.matchDstMac()).toLong(),
336 appIntent.bandwidth());
337 cspi.setPathFrozen(appIntent.isStaticPath());
338 intentOperations.add(operator, cspi);
339 } else {
340 log.error("Unknown Application Intent Type: {}",
341 appIntent.intentType());
342 return false;
343 }
344 removedApplicationIntentIds.remove(appIntentId);
345 }
346 // Apply the Intent Operations
347 executeIntentOperations(intentOperations);
348 return true;
349 }
350
351 /**
352 * Remove Application Intents.
353 *
354 * @param appId the Application ID to use.
355 * @param intentIds the Application Intent IDs to remove.
356 * @return true on success, otherwise false.
357 */
358 @Override
359 public boolean removeApplicationIntents(final String appId,
360 Collection<String> intentIds) {
361 IntentMap intentMap = getHighLevelIntents();
Pavlin Radoslavovda147842014-06-13 20:08:09 -0700362 List<String> removeIntentIds = new LinkedList<String>();
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700363
364 //
365 // Process all intents one-by-one
366 //
367 IntentOperationList operations = new IntentOperationList();
368 for (String intentId : intentIds) {
369 String appIntentId = appId + ":" + intentId;
370 Intent intent = intentMap.getIntent(appIntentId);
371 if (intent != null) {
Pavlin Radoslavovda147842014-06-13 20:08:09 -0700372 if (intent.getState() == IntentState.INST_NACK) {
373 // TODO: A hack to remove intents stuck in INST_NACK state
374 removeIntentIds.add(intent.getId());
375 continue;
376 }
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700377 operations.add(IntentOperation.Operator.REMOVE, intent);
378 removedApplicationIntentIds.add(appIntentId);
379 }
380 }
Pavlin Radoslavovda147842014-06-13 20:08:09 -0700381
382 // Purge intents
383 if (!removeIntentIds.isEmpty()) {
384 lock.lock(); // TODO optimize locking using smaller steps
385 try {
386 highLevelIntents.purge(removeIntentIds);
387 } finally {
388 lock.unlock();
389 }
390 }
391
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700392 executeIntentOperations(operations);
393
394 return true;
395 }
396
397 /**
398 * Remove all Application Intents.
399 *
400 * @param appId the Application ID to use.
401 * @return true on success, otherwise false.
402 */
403 @Override
404 public boolean removeAllApplicationIntents(final String appId) {
405 IntentMap intentMap = getHighLevelIntents();
Pavlin Radoslavovda147842014-06-13 20:08:09 -0700406 List<String> removeIntentIds = new LinkedList<String>();
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700407
408 //
409 // Remove all intents
410 //
411 IntentOperationList operations = new IntentOperationList();
412 for (Intent intent : intentMap.getAllIntents()) {
Pavlin Radoslavovda147842014-06-13 20:08:09 -0700413 if (intent.getState() == IntentState.INST_NACK) {
414 // TODO: A hack to remove intents stuck in INST_NACK state
415 removeIntentIds.add(intent.getId());
416 continue;
417 }
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700418 operations.add(IntentOperation.Operator.REMOVE, intent);
419 removedApplicationIntentIds.add(intent.getId());
420 }
Pavlin Radoslavovda147842014-06-13 20:08:09 -0700421
422 // Purge intents
423 if (!removeIntentIds.isEmpty()) {
424 lock.lock(); // TODO optimize locking using smaller steps
425 try {
426 highLevelIntents.purge(removeIntentIds);
427 } finally {
428 lock.unlock();
429 }
430 }
431
Pavlin Radoslavove2238bc2014-06-09 18:05:23 -0700432 executeIntentOperations(operations);
433
434 return true;
435 }
Toshio Koide798bc1b2014-02-20 14:02:40 -0800436
Ray Milkey269ffb92014-04-03 14:43:30 -0700437 @Override
438 public IntentOperationList executeIntentOperations(IntentOperationList list) {
TeruU9e530662014-05-18 11:49:37 -0700439
Ray Milkeyb29e6262014-04-09 16:02:14 -0700440 if (list == null || list.size() == 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700441 return null;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700442 }
Toshio Koide275d8142014-02-24 16:41:52 -0800443
Ray Milkey269ffb92014-04-03 14:43:30 -0700444 lock.lock(); // TODO optimize locking using smaller steps
445 try {
TeruU9e530662014-05-18 11:49:37 -0700446 log.trace("lock executeIntentOperations, lock obj is already locked? {}", lock.isLocked());
Ray Milkey269ffb92014-04-03 14:43:30 -0700447 // update the map of high-level intents
TeruU9e530662014-05-18 11:49:37 -0700448
Ray Milkey269ffb92014-04-03 14:43:30 -0700449 highLevelIntents.executeOperations(list);
Toshio Koidedf2eab92014-02-20 11:24:59 -0800450
Ray Milkey269ffb92014-04-03 14:43:30 -0700451 // change states of high-level intents
452 IntentStateList states = new IntentStateList();
453 for (IntentOperation op : list) {
454 switch (op.operator) {
455 case ADD:
456 switch (op.intent.getState()) {
457 case CREATED:
458 states.put(op.intent.getId(), IntentState.INST_REQ);
459 break;
460 case INST_ACK:
461 states.put(op.intent.getId(), IntentState.REROUTE_REQ);
462 break;
463 default:
464 break;
465 }
466 break;
467 case REMOVE:
468 switch (op.intent.getState()) {
469 case CREATED:
470 states.put(op.intent.getId(), IntentState.DEL_REQ);
471 break;
472 default:
473 break;
474 }
475 break;
476 default:
477 break;
478 }
479 }
480 highLevelIntents.changeStates(states);
Toshio Koidedf2eab92014-02-20 11:24:59 -0800481
Ray Milkey269ffb92014-04-03 14:43:30 -0700482 // calculate path-intents (low-level operations)
Ray Milkey269ffb92014-04-03 14:43:30 -0700483 IntentOperationList pathIntentOperations = runtime.calcPathIntents(list, highLevelIntents, pathIntents);
Toshio Koide600ae5f2014-02-20 18:42:00 -0800484
Ray Milkey269ffb92014-04-03 14:43:30 -0700485 // persist calculated low-level operations into data store
Ray Milkey269ffb92014-04-03 14:43:30 -0700486 long key = persistIntent.getKey();
487 persistIntent.persistIfLeader(key, pathIntentOperations);
Toshio Koide93be5d62014-02-23 19:30:57 -0800488
Ray Milkey269ffb92014-04-03 14:43:30 -0700489 // remove error-intents and reflect them to high-level intents
Ray Milkey269ffb92014-04-03 14:43:30 -0700490 states.clear();
491 Iterator<IntentOperation> i = pathIntentOperations.iterator();
492 while (i.hasNext()) {
493 IntentOperation op = i.next();
494 if (op.operator.equals(Operator.ERROR)) {
495 states.put(op.intent.getId(), IntentState.INST_NACK);
496 i.remove();
497 }
498 }
499 highLevelIntents.changeStates(states);
Toshio Koidea94060f2014-02-21 22:58:32 -0800500
Ray Milkey269ffb92014-04-03 14:43:30 -0700501 // update the map of path intents and publish the path operations
Ray Milkey269ffb92014-04-03 14:43:30 -0700502 pathIntents.executeOperations(pathIntentOperations);
Toshio Koide93be5d62014-02-23 19:30:57 -0800503
Ray Milkey269ffb92014-04-03 14:43:30 -0700504 // send notification
Ray Milkey269ffb92014-04-03 14:43:30 -0700505 // XXX: Send notifications using the same key every time
506 // and receive them by entryAdded() and entryUpdated()
507 opEventChannel.addEntry(0L, pathIntentOperations);
Ray Milkey269ffb92014-04-03 14:43:30 -0700508 //opEventChannel.removeEntry(key);
509 return pathIntentOperations;
510 } finally {
Ray Milkey269ffb92014-04-03 14:43:30 -0700511 lock.unlock();
TeruU9e530662014-05-18 11:49:37 -0700512 log.trace("unlock executeIntentOperations");
Ray Milkey269ffb92014-04-03 14:43:30 -0700513 }
514 }
Toshio Koide27ffd412014-02-18 19:15:27 -0800515
Ray Milkey269ffb92014-04-03 14:43:30 -0700516 @Override
517 public IntentMap getHighLevelIntents() {
518 return highLevelIntents;
519 }
Toshio Koide27ffd412014-02-18 19:15:27 -0800520
Ray Milkey269ffb92014-04-03 14:43:30 -0700521 @Override
522 public IntentMap getPathIntents() {
523 return pathIntents;
524 }
Toshio Koide27ffd412014-02-18 19:15:27 -0800525
Ray Milkey269ffb92014-04-03 14:43:30 -0700526 @Override
527 public void purgeIntents() {
528 highLevelIntents.purge();
529 pathIntents.purge();
530 }
Toshio Koide0c9106d2014-02-19 15:26:38 -0800531
Ray Milkey269ffb92014-04-03 14:43:30 -0700532 // ================================================================================
Jonathan Harte37e4e22014-05-13 19:12:02 -0700533 // ITopologyListener implementations
Ray Milkey269ffb92014-04-03 14:43:30 -0700534 // ================================================================================
Toshio Koide798bc1b2014-02-20 14:02:40 -0800535
Ray Milkeya5450cc2014-04-17 14:31:30 -0700536 // CHECKSTYLE:OFF suppress warning about too many parameters
Ray Milkey269ffb92014-04-03 14:43:30 -0700537 @Override
Jonathan Harte37e4e22014-05-13 19:12:02 -0700538 public void topologyEvents(Collection<SwitchEvent> addedSwitchEvents,
Ray Milkey269ffb92014-04-03 14:43:30 -0700539 Collection<SwitchEvent> removedSwitchEvents,
540 Collection<PortEvent> addedPortEvents,
541 Collection<PortEvent> removedPortEvents,
542 Collection<LinkEvent> addedLinkEvents,
543 Collection<LinkEvent> removedLinkEvents,
544 Collection<DeviceEvent> addedDeviceEvents,
545 Collection<DeviceEvent> removedDeviceEvents) {
Ray Milkeya5450cc2014-04-17 14:31:30 -0700546 // CHECKSTYLE:ON
Toshio Koidea9078af2014-02-21 16:57:04 -0800547
Ray Milkey269ffb92014-04-03 14:43:30 -0700548 PerfLogger p = new PerfLogger("networkGraphEvents");
549 HashSet<Intent> affectedPaths = new HashSet<>();
Toshio Koide93797dc2014-02-27 23:54:26 -0800550
Ray Milkey269ffb92014-04-03 14:43:30 -0700551 boolean rerouteAll = false;
552 for (LinkEvent le : addedLinkEvents) {
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700553 LinkEvent rev = new LinkEvent(le.getDst().getDpid(),
554 le.getDst().getNumber(), le.getSrc().getDpid(),
555 le.getSrc().getNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700556 if (unmatchedLinkEvents.contains(rev)) {
557 rerouteAll = true;
558 unmatchedLinkEvents.remove(rev);
559 log.debug("Found matched LinkEvent: {} {}", rev, le);
560 } else {
561 unmatchedLinkEvents.add(le);
562 log.debug("Adding unmatched LinkEvent: {}", le);
563 }
564 }
565 for (LinkEvent le : removedLinkEvents) {
566 if (unmatchedLinkEvents.contains(le)) {
567 unmatchedLinkEvents.remove(le);
568 log.debug("Removing LinkEvent: {}", le);
569 }
570 }
571 if (unmatchedLinkEvents.size() > 0) {
572 log.debug("Unmatched link events: {} events", unmatchedLinkEvents.size());
573 }
Toshio Koidea9078af2014-02-21 16:57:04 -0800574
Ray Milkey7f1567c2014-04-08 13:53:32 -0700575 if (rerouteAll) { //addedLinkEvents.size() > 0) { // ||
Ray Milkey269ffb92014-04-03 14:43:30 -0700576// addedPortEvents.size() > 0 ||
577// addedSwitchEvents.size() > 0) {
578 p.log("begin_getAllIntents");
579 affectedPaths.addAll(getPathIntents().getAllIntents());
580 p.log("end_getAllIntents");
581 } else if (removedSwitchEvents.size() > 0 ||
582 removedLinkEvents.size() > 0 ||
583 removedPortEvents.size() > 0) {
584 p.log("begin_getIntentsByLink");
Ray Milkeyb29e6262014-04-09 16:02:14 -0700585 for (LinkEvent linkEvent : removedLinkEvents) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700586 affectedPaths.addAll(pathIntents.getIntentsByLink(linkEvent));
Ray Milkeyb29e6262014-04-09 16:02:14 -0700587 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700588 p.log("end_getIntentsByLink");
Toshio Koidea9078af2014-02-21 16:57:04 -0800589
Ray Milkey269ffb92014-04-03 14:43:30 -0700590 p.log("begin_getIntentsByPort");
Ray Milkeyb29e6262014-04-09 16:02:14 -0700591 for (PortEvent portEvent : removedPortEvents) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700592 affectedPaths.addAll(pathIntents.getIntentsByPort(portEvent.getDpid(), portEvent.getNumber()));
Ray Milkeyb29e6262014-04-09 16:02:14 -0700593 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700594 p.log("end_getIntentsByPort");
Toshio Koidea9078af2014-02-21 16:57:04 -0800595
Ray Milkey269ffb92014-04-03 14:43:30 -0700596 p.log("begin_getIntentsByDpid");
Ray Milkeyb29e6262014-04-09 16:02:14 -0700597 for (SwitchEvent switchEvent : removedSwitchEvents) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700598 affectedPaths.addAll(pathIntents.getIntentsByDpid(switchEvent.getDpid()));
Ray Milkeyb29e6262014-04-09 16:02:14 -0700599 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700600 p.log("end_getIntentsByDpid");
601 }
602 p.log("begin_reroutePaths");
603 reroutePaths(affectedPaths);
604 p.log("end_reroutePaths");
605 p.flushLog();
606 }
Toshio Koide066506e2014-02-20 19:52:09 -0800607
Ray Milkey269ffb92014-04-03 14:43:30 -0700608 // ================================================================================
609 // IEventChannelListener implementations
610 // ================================================================================
Toshio Koide066506e2014-02-20 19:52:09 -0800611
Ray Milkey269ffb92014-04-03 14:43:30 -0700612 @Override
613 public void entryAdded(IntentStateList value) {
614 entryUpdated(value);
615 }
Toshio Koide066506e2014-02-20 19:52:09 -0800616
Ray Milkey269ffb92014-04-03 14:43:30 -0700617 @Override
618 public void entryRemoved(IntentStateList value) {
619 // do nothing
620 }
Toshio Koide066506e2014-02-20 19:52:09 -0800621
Ray Milkey149693c2014-05-20 14:58:53 -0700622 @SuppressWarnings("fallthrough")
Ray Milkey269ffb92014-04-03 14:43:30 -0700623 @Override
624 public void entryUpdated(IntentStateList value) {
625 // TODO draw state transition diagram in multiple ONOS instances and update this method
TeruU9e530662014-05-18 11:49:37 -0700626
Toshio Koide353a9e12014-06-09 21:03:40 -0700627 IntentOperationList opList = new IntentOperationList();
Ray Milkey269ffb92014-04-03 14:43:30 -0700628 lock.lock(); // TODO optimize locking using smaller steps
629 try {
TeruU9e530662014-05-18 11:49:37 -0700630 log.trace("lock entryUpdated, lock obj is already locked? {}", lock.isLocked());
Ray Milkey269ffb92014-04-03 14:43:30 -0700631 // reflect state changes of path-level intent into application-level intents
Ray Milkey269ffb92014-04-03 14:43:30 -0700632 IntentStateList highLevelIntentStates = new IntentStateList();
633 IntentStateList pathIntentStates = new IntentStateList();
634 for (Entry<String, IntentState> entry : value.entrySet()) {
Toshio Koide14dba902014-06-05 18:39:21 -0700635 String pathIntentId = entry.getKey();
636 IntentState nextPathIntentState = entry.getValue();
637 PathIntent pathIntent = (PathIntent) pathIntents.getIntent(pathIntentId);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700638 if (pathIntent == null) {
639 continue;
640 }
Toshio Koide8315d7d2014-02-21 22:58:32 -0800641
Ray Milkey269ffb92014-04-03 14:43:30 -0700642 Intent parentIntent = pathIntent.getParentIntent();
643 if (parentIntent == null ||
Toshio Koide14dba902014-06-05 18:39:21 -0700644 !(parentIntent instanceof ShortestPathIntent)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700645 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700646 }
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700647 String parentIntentId = parentIntent.getId();
Toshio Koide066506e2014-02-20 19:52:09 -0800648
Toshio Koide14dba902014-06-05 18:39:21 -0700649 boolean isChildIntent = ((ShortestPathIntent) parentIntent).getPathIntentId().equals(pathIntentId);
Toshio Koide353a9e12014-06-09 21:03:40 -0700650
651 // Check necessity for retrying the intent execution.
652 // When the PathIntent(=isChildIntent) transitioned to INST_{ACK/NACK}
653 // but was marked as stale (e.g., has been requested to reroute by Topology event),
654 // then immediately enqueue the re-computation of parent intent.
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700655 if (isChildIntent && staleIntents.containsKey(parentIntentId) && (
Toshio Koide353a9e12014-06-09 21:03:40 -0700656 nextPathIntentState.equals(IntentState.INST_ACK) ||
657 nextPathIntentState.equals(IntentState.INST_NACK))) {
658 opList.add(Operator.ADD, parentIntent);
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700659 staleIntents.remove(parentIntentId);
660 log.debug("retrying intent execution for intent ID:{}", parentIntentId);
Toshio Koide353a9e12014-06-09 21:03:40 -0700661 }
662
Toshio Koide14dba902014-06-05 18:39:21 -0700663 switch (nextPathIntentState) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700664 case INST_ACK:
TeruUf9111652014-05-14 23:10:35 -0700665 Set<Long> installedDpids = calcInstalledDpids(pathIntent, value.domainSwitchDpids);
666 if (!isFlowInstalled(pathIntent, installedDpids)) {
667 break;
668 }
669 // FALLTHROUGH
Ray Milkey269ffb92014-04-03 14:43:30 -0700670 case INST_NACK:
TeruUf9111652014-05-14 23:10:35 -0700671 // FALLTHROUGH
Ray Milkey269ffb92014-04-03 14:43:30 -0700672 case DEL_PENDING:
Toshio Koide14dba902014-06-05 18:39:21 -0700673 if (isChildIntent) {
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700674 log.debug("put the state highLevelIntentStates ID {}, state {}",
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700675 parentIntentId, nextPathIntentState);
676 highLevelIntentStates.put(parentIntentId, nextPathIntentState);
Toshio Koide14dba902014-06-05 18:39:21 -0700677 }
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700678 log.debug("put the state pathIntentStates ID {}, state {}",
679 pathIntentId, nextPathIntentState);
Toshio Koide14dba902014-06-05 18:39:21 -0700680 pathIntentStates.put(pathIntentId, nextPathIntentState);
TeruU9e530662014-05-18 11:49:37 -0700681 break;
682 case DEL_ACK:
Toshio Koide14dba902014-06-05 18:39:21 -0700683 if (isChildIntent) {
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700684 if (intentInstalledMap.containsKey(pathIntentId)) {
685 intentInstalledMap.remove(pathIntentId);
Toshio Koide14dba902014-06-05 18:39:21 -0700686 }
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700687 log.debug("put the state highLevelIntentStates ID {}, state {}",
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700688 parentIntentId, nextPathIntentState);
689 highLevelIntentStates.put(parentIntentId, nextPathIntentState);
TeruU9e530662014-05-18 11:49:37 -0700690 }
Jonathan Hartc00f5c22014-06-10 15:14:40 -0700691 log.debug("put the state pathIntentStates ID {}, state {}",
692 pathIntentId, nextPathIntentState);
Toshio Koide14dba902014-06-05 18:39:21 -0700693 pathIntentStates.put(pathIntentId, nextPathIntentState);
694 break;
695 case CREATED:
696 break;
697 case DEL_REQ:
698 break;
699 case INST_REQ:
700 break;
701 case REROUTE_REQ:
Ray Milkey269ffb92014-04-03 14:43:30 -0700702 break;
703 default:
704 break;
705 }
706 }
707 highLevelIntents.changeStates(highLevelIntentStates);
708 pathIntents.changeStates(pathIntentStates);
Ray Milkey269ffb92014-04-03 14:43:30 -0700709 } finally {
Ray Milkey269ffb92014-04-03 14:43:30 -0700710 lock.unlock();
TeruU9e530662014-05-18 11:49:37 -0700711 log.trace("unlock entryUpdated");
Ray Milkey269ffb92014-04-03 14:43:30 -0700712 }
Toshio Koide353a9e12014-06-09 21:03:40 -0700713 executeIntentOperations(opList);
Ray Milkey269ffb92014-04-03 14:43:30 -0700714 }
TeruUf9111652014-05-14 23:10:35 -0700715
716 /***
717 * This function is to check whether the entire path's flow entries are installed or not.
TeruU9e530662014-05-18 11:49:37 -0700718 *
TeruUf9111652014-05-14 23:10:35 -0700719 * @param pathIntent : The pathIntent to be checked
720 * @param installedDpids : The dpids installed on one ONOS instance
721 * @return The result of whether a pathIntent has been installed or not.
722 */
723 private boolean isFlowInstalled(PathIntent pathIntent, Set<Long> installedDpids) {
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700724 String pathIntentId = pathIntent.getId();
TeruUf9111652014-05-14 23:10:35 -0700725
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700726 if (intentInstalledMap.containsKey(pathIntentId)) {
TeruUf9111652014-05-14 23:10:35 -0700727 if (!installedDpids.isEmpty()) {
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700728 intentInstalledMap.get(pathIntentId).addAll(installedDpids);
TeruUf9111652014-05-14 23:10:35 -0700729 }
730 } else {
731 // This is the creation of an entry.
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700732 intentInstalledMap.put(pathIntentId, installedDpids);
TeruUf9111652014-05-14 23:10:35 -0700733 }
734
735 Set<Long> allSwitchesForPath = new HashSet<Long>();
736 ShortestPathIntent spfIntent = (ShortestPathIntent) pathIntent.getParentIntent();
737
738 for (LinkEvent linkEvent : pathIntent.getPath()) {
739 long sw = linkEvent.getSrc().getDpid();
740 allSwitchesForPath.add(sw);
741 }
742 allSwitchesForPath.add(spfIntent.getDstSwitchDpid());
743
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700744 if (log.isDebugEnabled()) {
745 log.debug("checking flow installation. ID:{}, dpids:{}, installed:{}",
746 pathIntentId,
747 allSwitchesForPath,
748 intentInstalledMap.get(pathIntentId));
TeruUf9111652014-05-14 23:10:35 -0700749 }
750
Toshio Koide4a58f0a2014-06-16 10:34:19 -0700751 if (allSwitchesForPath.equals(intentInstalledMap.get(pathIntentId))) {
752 intentInstalledMap.remove(pathIntentId);
TeruUf9111652014-05-14 23:10:35 -0700753 return true;
754 }
755
756 return false;
757 }
758
759 private Set<Long> calcInstalledDpids(PathIntent pathIntent, Set<Long> domainSwitchDpids) {
760 Set<Long> allSwitchesForPath = new HashSet<Long>();
761 ShortestPathIntent spfIntent = (ShortestPathIntent) pathIntent.getParentIntent();
762
763 for (LinkEvent linkEvent : pathIntent.getPath()) {
764 long sw = linkEvent.getSrc().getDpid();
765
766 if (domainSwitchDpids.contains(sw)) {
767 allSwitchesForPath.add(sw);
768 }
769 }
770
771 if (domainSwitchDpids.contains(spfIntent.getDstSwitchDpid())) {
772 allSwitchesForPath.add(spfIntent.getDstSwitchDpid());
773 }
774
775 if (log.isTraceEnabled()) {
TeruU9e530662014-05-18 11:49:37 -0700776 log.trace("All switches for a path {}, domain switch dpids {}", allSwitchesForPath, domainSwitchDpids);
TeruUf9111652014-05-14 23:10:35 -0700777 }
778
779 return allSwitchesForPath;
780 }
Toshio Koidea9078af2014-02-21 16:57:04 -0800781}