blob: 5f6bc5d5d80de021003d764c73c25140ec1358dd [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.intent.impl;
tom95329eb2014-10-06 08:40:06 -070017
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080018import com.google.common.collect.HashMultimap;
19import com.google.common.collect.Lists;
Thomas Vachuskac46af202015-06-03 16:43:27 -070020import com.google.common.collect.Maps;
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080021import com.google.common.collect.SetMultimap;
tom95329eb2014-10-06 08:40:06 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070027import org.apache.felix.scr.annotations.ReferencePolicy;
tom95329eb2014-10-06 08:40:06 -070028import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080029import org.onosproject.core.ApplicationId;
30import org.onosproject.event.Event;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070031import org.onosproject.net.DeviceId;
32import org.onosproject.net.ElementId;
33import org.onosproject.net.HostId;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.Link;
35import org.onosproject.net.LinkKey;
36import org.onosproject.net.NetworkResource;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070037import org.onosproject.net.device.DeviceEvent;
38import org.onosproject.net.device.DeviceListener;
39import org.onosproject.net.device.DeviceService;
40import org.onosproject.net.host.HostEvent;
41import org.onosproject.net.host.HostListener;
42import org.onosproject.net.host.HostService;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070043import org.onosproject.net.intent.Intent;
Thomas Vachuskac46af202015-06-03 16:43:27 -070044import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.intent.IntentService;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080046import org.onosproject.net.intent.Key;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070047import org.onosproject.net.intent.PartitionEvent;
48import org.onosproject.net.intent.PartitionEventListener;
49import org.onosproject.net.intent.PartitionService;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.link.LinkEvent;
Brian O'Connor6de2e202015-05-21 14:30:41 -070051import org.onosproject.net.resource.link.LinkResourceEvent;
52import org.onosproject.net.resource.link.LinkResourceListener;
53import org.onosproject.net.resource.link.LinkResourceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import org.onosproject.net.topology.TopologyEvent;
55import org.onosproject.net.topology.TopologyListener;
56import org.onosproject.net.topology.TopologyService;
tom95329eb2014-10-06 08:40:06 -070057import org.slf4j.Logger;
58
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080059import java.util.Collection;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070060import java.util.Collections;
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080061import java.util.HashSet;
Thomas Vachuskac46af202015-06-03 16:43:27 -070062import java.util.List;
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080063import java.util.Set;
Thomas Vachuskac46af202015-06-03 16:43:27 -070064import java.util.concurrent.ConcurrentMap;
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080065import java.util.concurrent.ExecutorService;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070066import java.util.concurrent.Executors;
67import java.util.concurrent.ScheduledExecutorService;
68import java.util.concurrent.TimeUnit;
69import java.util.concurrent.atomic.AtomicBoolean;
tom95329eb2014-10-06 08:40:06 -070070
71import static com.google.common.base.Preconditions.checkArgument;
72import static com.google.common.base.Preconditions.checkNotNull;
73import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
74import static java.util.concurrent.Executors.newSingleThreadExecutor;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080075import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuskac46af202015-06-03 16:43:27 -070076import static org.onlab.util.Tools.isNullOrEmpty;
Brian O'Connorabafb502014-12-02 22:26:20 -080077import static org.onosproject.net.LinkKey.linkKey;
Thomas Vachuskac46af202015-06-03 16:43:27 -070078import static org.onosproject.net.intent.IntentState.INSTALLED;
Brian O'Connorabafb502014-12-02 22:26:20 -080079import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
80import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
tom95329eb2014-10-06 08:40:06 -070081import static org.slf4j.LoggerFactory.getLogger;
82
83/**
84 * Entity responsible for tracking installed flows and for monitoring topology
85 * events to determine what flows are affected by topology changes.
86 */
Ray Milkeye97ede92014-11-20 10:43:12 -080087@Component(immediate = true)
tom95329eb2014-10-06 08:40:06 -070088@Service
tom85258ee2014-10-07 00:10:02 -070089public class ObjectiveTracker implements ObjectiveTrackerService {
tom95329eb2014-10-06 08:40:06 -070090
91 private final Logger log = getLogger(getClass());
92
Thomas Vachuskac46af202015-06-03 16:43:27 -070093 private final ConcurrentMap<Key, Intent> intents = Maps.newConcurrentMap();
94
Ray Milkeyf9af43c2015-02-09 16:45:48 -080095 private final SetMultimap<LinkKey, Key> intentsByLink =
Brian O'Connor64a0369d2015-02-20 22:02:59 -080096 //TODO this could be slow as a point of synchronization
Ray Milkeyf9af43c2015-02-09 16:45:48 -080097 synchronizedSetMultimap(HashMultimap.<LinkKey, Key>create());
tom95329eb2014-10-06 08:40:06 -070098
Brian O'Connorb5dcc512015-03-24 17:28:00 -070099 private final SetMultimap<ElementId, Key> intentsByDevice =
100 synchronizedSetMultimap(HashMultimap.<ElementId, Key>create());
101
tom95329eb2014-10-06 08:40:06 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected TopologyService topologyService;
104
Ray Milkeye97ede92014-11-20 10:43:12 -0800105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected LinkResourceService resourceManager;
107
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DeviceService deviceService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected HostService hostService;
113
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700114 @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY,
115 policy = ReferencePolicy.DYNAMIC)
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800116 protected IntentService intentService;
117
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected PartitionService partitionService;
120
tom95329eb2014-10-06 08:40:06 -0700121 private ExecutorService executorService =
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700122 newSingleThreadExecutor(groupedThreads("onos/intent", "objectivetracker"));
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700123 private ScheduledExecutorService executor = Executors
124 .newScheduledThreadPool(1);
tom95329eb2014-10-06 08:40:06 -0700125
126 private TopologyListener listener = new InternalTopologyListener();
Ray Milkeye97ede92014-11-20 10:43:12 -0800127 private LinkResourceListener linkResourceListener =
128 new InternalLinkResourceListener();
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700129 private DeviceListener deviceListener = new InternalDeviceListener();
130 private HostListener hostListener = new InternalHostListener();
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700131 private PartitionEventListener partitionListener = new InternalPartitionListener();
tom95329eb2014-10-06 08:40:06 -0700132 private TopologyChangeDelegate delegate;
133
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700134 protected final AtomicBoolean updateScheduled = new AtomicBoolean(false);
135
tom95329eb2014-10-06 08:40:06 -0700136 @Activate
137 public void activate() {
138 topologyService.addListener(listener);
Ray Milkeye97ede92014-11-20 10:43:12 -0800139 resourceManager.addListener(linkResourceListener);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700140 deviceService.addListener(deviceListener);
141 hostService.addListener(hostListener);
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700142 partitionService.addListener(partitionListener);
tom95329eb2014-10-06 08:40:06 -0700143 log.info("Started");
144 }
145
146 @Deactivate
147 public void deactivate() {
148 topologyService.removeListener(listener);
Ray Milkeye97ede92014-11-20 10:43:12 -0800149 resourceManager.removeListener(linkResourceListener);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700150 deviceService.removeListener(deviceListener);
151 hostService.removeListener(hostListener);
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700152 partitionService.removeListener(partitionListener);
tom95329eb2014-10-06 08:40:06 -0700153 log.info("Stopped");
154 }
155
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800156 protected void bindIntentService(IntentService service) {
157 if (intentService == null) {
158 intentService = service;
159 }
160 }
161
162 protected void unbindIntentService(IntentService service) {
163 if (intentService == service) {
164 intentService = null;
165 }
166 }
167
tom95329eb2014-10-06 08:40:06 -0700168 @Override
169 public void setDelegate(TopologyChangeDelegate delegate) {
170 checkNotNull(delegate, "Delegate cannot be null");
171 checkArgument(this.delegate == null || this.delegate == delegate,
172 "Another delegate already set");
173 this.delegate = delegate;
174 }
175
176 @Override
177 public void unsetDelegate(TopologyChangeDelegate delegate) {
178 checkArgument(this.delegate == delegate, "Not the current delegate");
179 this.delegate = null;
180 }
181
182 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800183 public void addTrackedResources(Key intentKey,
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700184 Collection<NetworkResource> resources) {
185 for (NetworkResource resource : resources) {
186 if (resource instanceof Link) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800187 intentsByLink.put(linkKey((Link) resource), intentKey);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700188 } else if (resource instanceof ElementId) {
189 intentsByDevice.put((ElementId) resource, intentKey);
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700190 }
tom95329eb2014-10-06 08:40:06 -0700191 }
192 }
193
194 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800195 public void removeTrackedResources(Key intentKey,
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700196 Collection<NetworkResource> resources) {
197 for (NetworkResource resource : resources) {
198 if (resource instanceof Link) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800199 intentsByLink.remove(linkKey((Link) resource), intentKey);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700200 } else if (resource instanceof ElementId) {
201 intentsByDevice.remove((ElementId) resource, intentKey);
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700202 }
tom95329eb2014-10-06 08:40:06 -0700203 }
204 }
205
Thomas Vachuskac46af202015-06-03 16:43:27 -0700206 @Override
207 public void trackIntent(IntentData intentData) {
208
209 //NOTE: This will be called for intents that are being added to the store
210 // locally (i.e. every intent update)
211
212 Key key = intentData.key();
213 Intent intent = intentData.intent();
214 boolean isLocal = intentService.isLocal(key);
215 List<Intent> installables = intentData.installables();
216
217 if (log.isTraceEnabled()) {
218 log.trace("intent {}, old: {}, new: {}, installableCount: {}, resourceCount: {}",
219 key,
220 intentsByDevice.values().contains(key),
221 isLocal,
222 installables.size(),
223 intent.resources().size() +
224 installables.stream()
225 .mapToLong(i -> i.resources().size()).sum());
226 }
227
228 if (isNullOrEmpty(installables) && intentData.state() == INSTALLED) {
229 log.warn("Intent {} is INSTALLED with no installables", key);
230 }
231
232 if (isLocal) {
233 addTrackedResources(key, intent.resources());
234 for (Intent installable : installables) {
235 addTrackedResources(key, installable.resources());
236 }
237 // FIXME check all resources against current topo service(s); recompile if necessary
238 } else {
239 removeTrackedResources(key, intent.resources());
240 for (Intent installable : installables) {
241 removeTrackedResources(key, installable.resources());
242 }
243 }
244 }
245
tom95329eb2014-10-06 08:40:06 -0700246 // Internal re-actor to topology change events.
247 private class InternalTopologyListener implements TopologyListener {
248 @Override
249 public void event(TopologyEvent event) {
250 executorService.execute(new TopologyChangeHandler(event));
251 }
252 }
253
254 // Re-dispatcher of topology change events.
255 private class TopologyChangeHandler implements Runnable {
256
257 private final TopologyEvent event;
258
259 TopologyChangeHandler(TopologyEvent event) {
260 this.event = event;
261 }
262
263 @Override
264 public void run() {
Thomas Vachuska7b652ad2014-10-30 14:10:51 -0700265 // If there is no delegate, why bother? Just bail.
266 if (delegate == null) {
267 return;
268 }
269
Ray Milkey7c44c052014-12-05 10:34:54 -0800270 if (event.reasons() == null || event.reasons().isEmpty()) {
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700271 delegate.triggerCompile(Collections.emptySet(), true);
tom85258ee2014-10-07 00:10:02 -0700272
tom95329eb2014-10-06 08:40:06 -0700273 } else {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800274 Set<Key> toBeRecompiled = new HashSet<>();
tom85258ee2014-10-07 00:10:02 -0700275 boolean recompileOnly = true;
276
277 // Scan through the list of reasons and keep accruing all
278 // intents that need to be recompiled.
tom95329eb2014-10-06 08:40:06 -0700279 for (Event reason : event.reasons()) {
280 if (reason instanceof LinkEvent) {
281 LinkEvent linkEvent = (LinkEvent) reason;
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800282 if (linkEvent.type() == LINK_REMOVED
283 || (linkEvent.type() == LINK_UPDATED &&
284 linkEvent.subject().isDurable())) {
Yuta HIGUCHI56df70f2014-10-28 22:26:15 -0700285 final LinkKey linkKey = linkKey(linkEvent.subject());
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800286 synchronized (intentsByLink) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800287 Set<Key> intentKeys = intentsByLink.get(linkKey);
288 log.debug("recompile triggered by LinkDown {} {}", linkKey, intentKeys);
289 toBeRecompiled.addAll(intentKeys);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800290 }
tom85258ee2014-10-07 00:10:02 -0700291 }
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800292 recompileOnly = recompileOnly &&
293 (linkEvent.type() == LINK_REMOVED ||
294 (linkEvent.type() == LINK_UPDATED &&
295 linkEvent.subject().isDurable()));
tom95329eb2014-10-06 08:40:06 -0700296 }
297 }
tom85258ee2014-10-07 00:10:02 -0700298 delegate.triggerCompile(toBeRecompiled, !recompileOnly);
tom95329eb2014-10-06 08:40:06 -0700299 }
300 }
301 }
302
Ray Milkeye97ede92014-11-20 10:43:12 -0800303 /**
304 * Internal re-actor to resource available events.
305 */
306 private class InternalLinkResourceListener implements LinkResourceListener {
307 @Override
308 public void event(LinkResourceEvent event) {
309 executorService.execute(new ResourceAvailableHandler(event));
310 }
311 }
312
313 /*
314 * Re-dispatcher of resource available events.
315 */
316 private class ResourceAvailableHandler implements Runnable {
317
318 private final LinkResourceEvent event;
319
320 ResourceAvailableHandler(LinkResourceEvent event) {
321 this.event = event;
322 }
323
324 @Override
325 public void run() {
326 // If there is no delegate, why bother? Just bail.
327 if (delegate == null) {
328 return;
329 }
330
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700331 delegate.triggerCompile(Collections.emptySet(), true);
Ray Milkeye97ede92014-11-20 10:43:12 -0800332 }
333 }
334
Brian O'Connor72a034c2014-11-26 18:24:23 -0800335 //TODO consider adding flow rule event tracking
Ray Milkeye97ede92014-11-20 10:43:12 -0800336
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800337 private void updateTrackedResources(ApplicationId appId, boolean track) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800338 if (intentService == null) {
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700339 log.warn("Intent service is not bound yet");
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800340 return;
341 }
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800342 intentService.getIntents().forEach(intent -> {
343 if (intent.appId().equals(appId)) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800344 Key key = intent.key();
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800345 Collection<NetworkResource> resources = Lists.newArrayList();
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800346 intentService.getInstallableIntents(key).stream()
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800347 .map(installable -> installable.resources())
348 .forEach(resources::addAll);
349 if (track) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800350 addTrackedResources(key, resources);
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800351 } else {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800352 removeTrackedResources(key, resources);
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800353 }
354 }
355 });
356 }
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700357
358 /*
359 * Re-dispatcher of device and host events.
360 */
361 private class DeviceAvailabilityHandler implements Runnable {
362
363 private final ElementId id;
364 private final boolean available;
365
366 DeviceAvailabilityHandler(ElementId id, boolean available) {
367 this.id = checkNotNull(id);
368 this.available = available;
369 }
370
371 @Override
372 public void run() {
373 // If there is no delegate, why bother? Just bail.
374 if (delegate == null) {
375 return;
376 }
377
378 // TODO should we recompile on available==true?
379 delegate.triggerCompile(intentsByDevice.get(id), available);
380 }
381 }
382
383
384 private class InternalDeviceListener implements DeviceListener {
385 @Override
386 public void event(DeviceEvent event) {
387 DeviceEvent.Type type = event.type();
388 if (type == DeviceEvent.Type.PORT_ADDED ||
389 type == DeviceEvent.Type.PORT_UPDATED ||
390 type == DeviceEvent.Type.PORT_REMOVED) {
391 // skip port events for now
392 return;
393 }
394 DeviceId id = event.subject().id();
395 // TODO we need to check whether AVAILABILITY_CHANGED means up or down
396 boolean available = (type == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
397 type == DeviceEvent.Type.DEVICE_ADDED ||
398 type == DeviceEvent.Type.DEVICE_UPDATED);
399 executorService.execute(new DeviceAvailabilityHandler(id, available));
400
401 }
402 }
403
404 private class InternalHostListener implements HostListener {
405 @Override
406 public void event(HostEvent event) {
407 HostId id = event.subject().id();
408 HostEvent.Type type = event.type();
409 boolean available = (type == HostEvent.Type.HOST_ADDED);
410 executorService.execute(new DeviceAvailabilityHandler(id, available));
411 }
412 }
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700413
414 protected void doIntentUpdate() {
415 updateScheduled.set(false);
416 if (intentService == null) {
417 log.warn("Intent service is not bound yet");
418 return;
419 }
420 try {
421 //FIXME very inefficient
Thomas Vachuskac46af202015-06-03 16:43:27 -0700422 for (IntentData intentData : intentService.getIntentData()) {
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700423 try {
Thomas Vachuskac46af202015-06-03 16:43:27 -0700424 trackIntent(intentData);
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700425 } catch (NullPointerException npe) {
Thomas Vachuskac46af202015-06-03 16:43:27 -0700426 log.warn("intent error {}", intentData.key(), npe);
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700427 }
428 }
429 } catch (Exception e) {
430 log.warn("Exception caught during update task", e);
431 }
432 }
433
434 private void scheduleIntentUpdate(int afterDelaySec) {
435 if (updateScheduled.compareAndSet(false, true)) {
436 executor.schedule(this::doIntentUpdate, afterDelaySec, TimeUnit.SECONDS);
437 }
438 }
439
440 private final class InternalPartitionListener implements PartitionEventListener {
441 @Override
442 public void event(PartitionEvent event) {
Brian O'Connor3057f212015-05-29 18:22:18 -0700443 log.debug("got message {}", event.subject());
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700444 scheduleIntentUpdate(1);
445 }
446 }
tom95329eb2014-10-06 08:40:06 -0700447}