blob: 62d67ed7a9b110ba38bf0bb8e5d985e50b3b7ba8 [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;
20import com.google.common.collect.SetMultimap;
tom95329eb2014-10-06 08:40:06 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070026import org.apache.felix.scr.annotations.ReferencePolicy;
tom95329eb2014-10-06 08:40:06 -070027import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.core.ApplicationId;
29import org.onosproject.event.Event;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070030import org.onosproject.net.DeviceId;
31import org.onosproject.net.ElementId;
32import org.onosproject.net.HostId;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.Link;
34import org.onosproject.net.LinkKey;
35import org.onosproject.net.NetworkResource;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070036import org.onosproject.net.device.DeviceEvent;
37import org.onosproject.net.device.DeviceListener;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.host.HostEvent;
40import org.onosproject.net.host.HostListener;
41import org.onosproject.net.host.HostService;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070042import org.onosproject.net.intent.Intent;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.intent.IntentService;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080044import org.onosproject.net.intent.Key;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070045import org.onosproject.net.intent.PartitionEvent;
46import org.onosproject.net.intent.PartitionEventListener;
47import org.onosproject.net.intent.PartitionService;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.link.LinkEvent;
Brian O'Connor6de2e202015-05-21 14:30:41 -070049import org.onosproject.net.resource.link.LinkResourceEvent;
50import org.onosproject.net.resource.link.LinkResourceListener;
51import org.onosproject.net.resource.link.LinkResourceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.topology.TopologyEvent;
53import org.onosproject.net.topology.TopologyListener;
54import org.onosproject.net.topology.TopologyService;
tom95329eb2014-10-06 08:40:06 -070055import org.slf4j.Logger;
56
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080057import java.util.Collection;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070058import java.util.Collections;
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080059import java.util.HashSet;
60import java.util.Set;
61import java.util.concurrent.ExecutorService;
Brian O'Connor69d6ac72015-05-29 16:24:06 -070062import java.util.concurrent.Executors;
63import java.util.concurrent.ScheduledExecutorService;
64import java.util.concurrent.TimeUnit;
65import java.util.concurrent.atomic.AtomicBoolean;
tom95329eb2014-10-06 08:40:06 -070066
67import static com.google.common.base.Preconditions.checkArgument;
68import static com.google.common.base.Preconditions.checkNotNull;
69import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
70import static java.util.concurrent.Executors.newSingleThreadExecutor;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080071import static org.onlab.util.Tools.groupedThreads;
Brian O'Connorabafb502014-12-02 22:26:20 -080072import static org.onosproject.net.LinkKey.linkKey;
73import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
74import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
tom95329eb2014-10-06 08:40:06 -070075import static org.slf4j.LoggerFactory.getLogger;
76
77/**
78 * Entity responsible for tracking installed flows and for monitoring topology
79 * events to determine what flows are affected by topology changes.
80 */
Ray Milkeye97ede92014-11-20 10:43:12 -080081@Component(immediate = true)
tom95329eb2014-10-06 08:40:06 -070082@Service
tom85258ee2014-10-07 00:10:02 -070083public class ObjectiveTracker implements ObjectiveTrackerService {
tom95329eb2014-10-06 08:40:06 -070084
85 private final Logger log = getLogger(getClass());
86
Ray Milkeyf9af43c2015-02-09 16:45:48 -080087 private final SetMultimap<LinkKey, Key> intentsByLink =
Brian O'Connor64a0369d2015-02-20 22:02:59 -080088 //TODO this could be slow as a point of synchronization
Ray Milkeyf9af43c2015-02-09 16:45:48 -080089 synchronizedSetMultimap(HashMultimap.<LinkKey, Key>create());
tom95329eb2014-10-06 08:40:06 -070090
Brian O'Connorb5dcc512015-03-24 17:28:00 -070091 private final SetMultimap<ElementId, Key> intentsByDevice =
92 synchronizedSetMultimap(HashMultimap.<ElementId, Key>create());
93
tom95329eb2014-10-06 08:40:06 -070094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected TopologyService topologyService;
96
Ray Milkeye97ede92014-11-20 10:43:12 -080097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected LinkResourceService resourceManager;
99
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected DeviceService deviceService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected HostService hostService;
105
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700106 @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY,
107 policy = ReferencePolicy.DYNAMIC)
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800108 protected IntentService intentService;
109
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected PartitionService partitionService;
112
tom95329eb2014-10-06 08:40:06 -0700113 private ExecutorService executorService =
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700114 newSingleThreadExecutor(groupedThreads("onos/intent", "objectivetracker"));
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700115 private ScheduledExecutorService executor = Executors
116 .newScheduledThreadPool(1);
tom95329eb2014-10-06 08:40:06 -0700117
118 private TopologyListener listener = new InternalTopologyListener();
Ray Milkeye97ede92014-11-20 10:43:12 -0800119 private LinkResourceListener linkResourceListener =
120 new InternalLinkResourceListener();
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700121 private DeviceListener deviceListener = new InternalDeviceListener();
122 private HostListener hostListener = new InternalHostListener();
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700123 private PartitionEventListener partitionListener = new InternalPartitionListener();
tom95329eb2014-10-06 08:40:06 -0700124 private TopologyChangeDelegate delegate;
125
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700126 protected final AtomicBoolean updateScheduled = new AtomicBoolean(false);
127
tom95329eb2014-10-06 08:40:06 -0700128 @Activate
129 public void activate() {
130 topologyService.addListener(listener);
Ray Milkeye97ede92014-11-20 10:43:12 -0800131 resourceManager.addListener(linkResourceListener);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700132 deviceService.addListener(deviceListener);
133 hostService.addListener(hostListener);
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700134 partitionService.addListener(partitionListener);
tom95329eb2014-10-06 08:40:06 -0700135 log.info("Started");
136 }
137
138 @Deactivate
139 public void deactivate() {
140 topologyService.removeListener(listener);
Ray Milkeye97ede92014-11-20 10:43:12 -0800141 resourceManager.removeListener(linkResourceListener);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700142 deviceService.removeListener(deviceListener);
143 hostService.removeListener(hostListener);
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700144 partitionService.removeListener(partitionListener);
tom95329eb2014-10-06 08:40:06 -0700145 log.info("Stopped");
146 }
147
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800148 protected void bindIntentService(IntentService service) {
149 if (intentService == null) {
150 intentService = service;
151 }
152 }
153
154 protected void unbindIntentService(IntentService service) {
155 if (intentService == service) {
156 intentService = null;
157 }
158 }
159
tom95329eb2014-10-06 08:40:06 -0700160 @Override
161 public void setDelegate(TopologyChangeDelegate delegate) {
162 checkNotNull(delegate, "Delegate cannot be null");
163 checkArgument(this.delegate == null || this.delegate == delegate,
164 "Another delegate already set");
165 this.delegate = delegate;
166 }
167
168 @Override
169 public void unsetDelegate(TopologyChangeDelegate delegate) {
170 checkArgument(this.delegate == delegate, "Not the current delegate");
171 this.delegate = null;
172 }
173
174 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800175 public void addTrackedResources(Key intentKey,
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700176 Collection<NetworkResource> resources) {
177 for (NetworkResource resource : resources) {
178 if (resource instanceof Link) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800179 intentsByLink.put(linkKey((Link) resource), intentKey);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700180 } else if (resource instanceof ElementId) {
181 intentsByDevice.put((ElementId) resource, intentKey);
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700182 }
tom95329eb2014-10-06 08:40:06 -0700183 }
184 }
185
186 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800187 public void removeTrackedResources(Key intentKey,
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700188 Collection<NetworkResource> resources) {
189 for (NetworkResource resource : resources) {
190 if (resource instanceof Link) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800191 intentsByLink.remove(linkKey((Link) resource), intentKey);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700192 } else if (resource instanceof ElementId) {
193 intentsByDevice.remove((ElementId) resource, intentKey);
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700194 }
tom95329eb2014-10-06 08:40:06 -0700195 }
196 }
197
198 // Internal re-actor to topology change events.
199 private class InternalTopologyListener implements TopologyListener {
200 @Override
201 public void event(TopologyEvent event) {
202 executorService.execute(new TopologyChangeHandler(event));
203 }
204 }
205
206 // Re-dispatcher of topology change events.
207 private class TopologyChangeHandler implements Runnable {
208
209 private final TopologyEvent event;
210
211 TopologyChangeHandler(TopologyEvent event) {
212 this.event = event;
213 }
214
215 @Override
216 public void run() {
Thomas Vachuska7b652ad2014-10-30 14:10:51 -0700217 // If there is no delegate, why bother? Just bail.
218 if (delegate == null) {
219 return;
220 }
221
Ray Milkey7c44c052014-12-05 10:34:54 -0800222 if (event.reasons() == null || event.reasons().isEmpty()) {
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700223 delegate.triggerCompile(Collections.emptySet(), true);
tom85258ee2014-10-07 00:10:02 -0700224
tom95329eb2014-10-06 08:40:06 -0700225 } else {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800226 Set<Key> toBeRecompiled = new HashSet<>();
tom85258ee2014-10-07 00:10:02 -0700227 boolean recompileOnly = true;
228
229 // Scan through the list of reasons and keep accruing all
230 // intents that need to be recompiled.
tom95329eb2014-10-06 08:40:06 -0700231 for (Event reason : event.reasons()) {
232 if (reason instanceof LinkEvent) {
233 LinkEvent linkEvent = (LinkEvent) reason;
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800234 if (linkEvent.type() == LINK_REMOVED
235 || (linkEvent.type() == LINK_UPDATED &&
236 linkEvent.subject().isDurable())) {
Yuta HIGUCHI56df70f2014-10-28 22:26:15 -0700237 final LinkKey linkKey = linkKey(linkEvent.subject());
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800238 synchronized (intentsByLink) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800239 Set<Key> intentKeys = intentsByLink.get(linkKey);
240 log.debug("recompile triggered by LinkDown {} {}", linkKey, intentKeys);
241 toBeRecompiled.addAll(intentKeys);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800242 }
tom85258ee2014-10-07 00:10:02 -0700243 }
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800244 recompileOnly = recompileOnly &&
245 (linkEvent.type() == LINK_REMOVED ||
246 (linkEvent.type() == LINK_UPDATED &&
247 linkEvent.subject().isDurable()));
tom95329eb2014-10-06 08:40:06 -0700248 }
249 }
tom85258ee2014-10-07 00:10:02 -0700250 delegate.triggerCompile(toBeRecompiled, !recompileOnly);
tom95329eb2014-10-06 08:40:06 -0700251 }
252 }
253 }
254
Ray Milkeye97ede92014-11-20 10:43:12 -0800255 /**
256 * Internal re-actor to resource available events.
257 */
258 private class InternalLinkResourceListener implements LinkResourceListener {
259 @Override
260 public void event(LinkResourceEvent event) {
261 executorService.execute(new ResourceAvailableHandler(event));
262 }
263 }
264
265 /*
266 * Re-dispatcher of resource available events.
267 */
268 private class ResourceAvailableHandler implements Runnable {
269
270 private final LinkResourceEvent event;
271
272 ResourceAvailableHandler(LinkResourceEvent event) {
273 this.event = event;
274 }
275
276 @Override
277 public void run() {
278 // If there is no delegate, why bother? Just bail.
279 if (delegate == null) {
280 return;
281 }
282
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700283 delegate.triggerCompile(Collections.emptySet(), true);
Ray Milkeye97ede92014-11-20 10:43:12 -0800284 }
285 }
286
Brian O'Connor72a034c2014-11-26 18:24:23 -0800287 //TODO consider adding flow rule event tracking
Ray Milkeye97ede92014-11-20 10:43:12 -0800288
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800289 private void updateTrackedResources(ApplicationId appId, boolean track) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800290 if (intentService == null) {
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700291 log.warn("Intent service is not bound yet");
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800292 return;
293 }
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800294 intentService.getIntents().forEach(intent -> {
295 if (intent.appId().equals(appId)) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800296 Key key = intent.key();
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800297 Collection<NetworkResource> resources = Lists.newArrayList();
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800298 intentService.getInstallableIntents(key).stream()
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800299 .map(installable -> installable.resources())
300 .forEach(resources::addAll);
301 if (track) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800302 addTrackedResources(key, resources);
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800303 } else {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800304 removeTrackedResources(key, resources);
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800305 }
306 }
307 });
308 }
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700309
310 /*
311 * Re-dispatcher of device and host events.
312 */
313 private class DeviceAvailabilityHandler implements Runnable {
314
315 private final ElementId id;
316 private final boolean available;
317
318 DeviceAvailabilityHandler(ElementId id, boolean available) {
319 this.id = checkNotNull(id);
320 this.available = available;
321 }
322
323 @Override
324 public void run() {
325 // If there is no delegate, why bother? Just bail.
326 if (delegate == null) {
327 return;
328 }
329
330 // TODO should we recompile on available==true?
331 delegate.triggerCompile(intentsByDevice.get(id), available);
332 }
333 }
334
335
336 private class InternalDeviceListener implements DeviceListener {
337 @Override
338 public void event(DeviceEvent event) {
339 DeviceEvent.Type type = event.type();
340 if (type == DeviceEvent.Type.PORT_ADDED ||
341 type == DeviceEvent.Type.PORT_UPDATED ||
342 type == DeviceEvent.Type.PORT_REMOVED) {
343 // skip port events for now
344 return;
345 }
346 DeviceId id = event.subject().id();
347 // TODO we need to check whether AVAILABILITY_CHANGED means up or down
348 boolean available = (type == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
349 type == DeviceEvent.Type.DEVICE_ADDED ||
350 type == DeviceEvent.Type.DEVICE_UPDATED);
351 executorService.execute(new DeviceAvailabilityHandler(id, available));
352
353 }
354 }
355
356 private class InternalHostListener implements HostListener {
357 @Override
358 public void event(HostEvent event) {
359 HostId id = event.subject().id();
360 HostEvent.Type type = event.type();
361 boolean available = (type == HostEvent.Type.HOST_ADDED);
362 executorService.execute(new DeviceAvailabilityHandler(id, available));
363 }
364 }
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700365
366 protected void doIntentUpdate() {
367 updateScheduled.set(false);
368 if (intentService == null) {
369 log.warn("Intent service is not bound yet");
370 return;
371 }
372 try {
373 //FIXME very inefficient
374 for (Intent intent : intentService.getIntents()) {
375 try {
376 if (intentService.isLocal(intent.key())) {
Brian O'Connor3057f212015-05-29 18:22:18 -0700377 log.trace("intent {}, old: {}, new: {}",
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700378 intent.key(), intentsByDevice.values().contains(intent.key()), true);
379 addTrackedResources(intent.key(), intent.resources());
380 intentService.getInstallableIntents(intent.key()).stream()
381 .forEach(installable ->
382 addTrackedResources(intent.key(), installable.resources()));
383 } else {
Brian O'Connor3057f212015-05-29 18:22:18 -0700384 log.trace("intent {}, old: {}, new: {}",
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700385 intent.key(), intentsByDevice.values().contains(intent.key()), false);
386 removeTrackedResources(intent.key(), intent.resources());
387 intentService.getInstallableIntents(intent.key()).stream()
388 .forEach(installable ->
389 removeTrackedResources(intent.key(), installable.resources()));
390 }
391 } catch (NullPointerException npe) {
392 log.warn("intent error {}", intent.key(), npe);
393 }
394 }
395 } catch (Exception e) {
396 log.warn("Exception caught during update task", e);
397 }
398 }
399
400 private void scheduleIntentUpdate(int afterDelaySec) {
401 if (updateScheduled.compareAndSet(false, true)) {
402 executor.schedule(this::doIntentUpdate, afterDelaySec, TimeUnit.SECONDS);
403 }
404 }
405
406 private final class InternalPartitionListener implements PartitionEventListener {
407 @Override
408 public void event(PartitionEvent event) {
Brian O'Connor3057f212015-05-29 18:22:18 -0700409 log.debug("got message {}", event.subject());
Brian O'Connor69d6ac72015-05-29 16:24:06 -0700410 scheduleIntentUpdate(1);
411 }
412 }
tom95329eb2014-10-06 08:40:06 -0700413}