blob: 939b9a1945ecb3111c2ad318f424feeb263ff077 [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;
26import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.core.ApplicationId;
28import org.onosproject.event.Event;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070029import org.onosproject.net.DeviceId;
30import org.onosproject.net.ElementId;
31import org.onosproject.net.HostId;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.Link;
33import org.onosproject.net.LinkKey;
34import org.onosproject.net.NetworkResource;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070035import org.onosproject.net.device.DeviceEvent;
36import org.onosproject.net.device.DeviceListener;
37import org.onosproject.net.device.DeviceService;
38import org.onosproject.net.host.HostEvent;
39import org.onosproject.net.host.HostListener;
40import org.onosproject.net.host.HostService;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.intent.IntentService;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080042import org.onosproject.net.intent.Key;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.link.LinkEvent;
Brian O'Connor6de2e202015-05-21 14:30:41 -070044import org.onosproject.net.resource.link.LinkResourceEvent;
45import org.onosproject.net.resource.link.LinkResourceListener;
46import org.onosproject.net.resource.link.LinkResourceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.topology.TopologyEvent;
48import org.onosproject.net.topology.TopologyListener;
49import org.onosproject.net.topology.TopologyService;
tom95329eb2014-10-06 08:40:06 -070050import org.slf4j.Logger;
51
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080052import java.util.Collection;
Brian O'Connorb5dcc512015-03-24 17:28:00 -070053import java.util.Collections;
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080054import java.util.HashSet;
55import java.util.Set;
56import java.util.concurrent.ExecutorService;
tom95329eb2014-10-06 08:40:06 -070057
58import static com.google.common.base.Preconditions.checkArgument;
59import static com.google.common.base.Preconditions.checkNotNull;
60import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
61import static java.util.concurrent.Executors.newSingleThreadExecutor;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080062import static org.onlab.util.Tools.groupedThreads;
Brian O'Connorabafb502014-12-02 22:26:20 -080063import static org.onosproject.net.LinkKey.linkKey;
64import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
65import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
tom95329eb2014-10-06 08:40:06 -070066import static org.slf4j.LoggerFactory.getLogger;
67
68/**
69 * Entity responsible for tracking installed flows and for monitoring topology
70 * events to determine what flows are affected by topology changes.
71 */
Ray Milkeye97ede92014-11-20 10:43:12 -080072@Component(immediate = true)
tom95329eb2014-10-06 08:40:06 -070073@Service
tom85258ee2014-10-07 00:10:02 -070074public class ObjectiveTracker implements ObjectiveTrackerService {
tom95329eb2014-10-06 08:40:06 -070075
76 private final Logger log = getLogger(getClass());
77
Ray Milkeyf9af43c2015-02-09 16:45:48 -080078 private final SetMultimap<LinkKey, Key> intentsByLink =
Brian O'Connor64a0369d2015-02-20 22:02:59 -080079 //TODO this could be slow as a point of synchronization
Ray Milkeyf9af43c2015-02-09 16:45:48 -080080 synchronizedSetMultimap(HashMultimap.<LinkKey, Key>create());
tom95329eb2014-10-06 08:40:06 -070081
Brian O'Connorb5dcc512015-03-24 17:28:00 -070082 private final SetMultimap<ElementId, Key> intentsByDevice =
83 synchronizedSetMultimap(HashMultimap.<ElementId, Key>create());
84
tom95329eb2014-10-06 08:40:06 -070085 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected TopologyService topologyService;
87
Ray Milkeye97ede92014-11-20 10:43:12 -080088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected LinkResourceService resourceManager;
90
Brian O'Connorb5dcc512015-03-24 17:28:00 -070091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected DeviceService deviceService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected HostService hostService;
96
Brian O'Connor5d55ed42014-12-01 18:27:47 -080097 @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080098 protected IntentService intentService;
99
tom95329eb2014-10-06 08:40:06 -0700100 private ExecutorService executorService =
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700101 newSingleThreadExecutor(groupedThreads("onos/intent", "objectivetracker"));
tom95329eb2014-10-06 08:40:06 -0700102
103 private TopologyListener listener = new InternalTopologyListener();
Ray Milkeye97ede92014-11-20 10:43:12 -0800104 private LinkResourceListener linkResourceListener =
105 new InternalLinkResourceListener();
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700106 private DeviceListener deviceListener = new InternalDeviceListener();
107 private HostListener hostListener = new InternalHostListener();
tom95329eb2014-10-06 08:40:06 -0700108 private TopologyChangeDelegate delegate;
109
110 @Activate
111 public void activate() {
112 topologyService.addListener(listener);
Ray Milkeye97ede92014-11-20 10:43:12 -0800113 resourceManager.addListener(linkResourceListener);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700114 deviceService.addListener(deviceListener);
115 hostService.addListener(hostListener);
tom95329eb2014-10-06 08:40:06 -0700116 log.info("Started");
117 }
118
119 @Deactivate
120 public void deactivate() {
121 topologyService.removeListener(listener);
Ray Milkeye97ede92014-11-20 10:43:12 -0800122 resourceManager.removeListener(linkResourceListener);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700123 deviceService.removeListener(deviceListener);
124 hostService.removeListener(hostListener);
tom95329eb2014-10-06 08:40:06 -0700125 log.info("Stopped");
126 }
127
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800128 protected void bindIntentService(IntentService service) {
129 if (intentService == null) {
130 intentService = service;
131 }
132 }
133
134 protected void unbindIntentService(IntentService service) {
135 if (intentService == service) {
136 intentService = null;
137 }
138 }
139
tom95329eb2014-10-06 08:40:06 -0700140 @Override
141 public void setDelegate(TopologyChangeDelegate delegate) {
142 checkNotNull(delegate, "Delegate cannot be null");
143 checkArgument(this.delegate == null || this.delegate == delegate,
144 "Another delegate already set");
145 this.delegate = delegate;
146 }
147
148 @Override
149 public void unsetDelegate(TopologyChangeDelegate delegate) {
150 checkArgument(this.delegate == delegate, "Not the current delegate");
151 this.delegate = null;
152 }
153
154 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800155 public void addTrackedResources(Key intentKey,
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700156 Collection<NetworkResource> resources) {
157 for (NetworkResource resource : resources) {
158 if (resource instanceof Link) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800159 intentsByLink.put(linkKey((Link) resource), intentKey);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700160 } else if (resource instanceof ElementId) {
161 intentsByDevice.put((ElementId) resource, intentKey);
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700162 }
tom95329eb2014-10-06 08:40:06 -0700163 }
164 }
165
166 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800167 public void removeTrackedResources(Key intentKey,
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700168 Collection<NetworkResource> resources) {
169 for (NetworkResource resource : resources) {
170 if (resource instanceof Link) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800171 intentsByLink.remove(linkKey((Link) resource), intentKey);
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700172 } else if (resource instanceof ElementId) {
173 intentsByDevice.remove((ElementId) resource, intentKey);
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700174 }
tom95329eb2014-10-06 08:40:06 -0700175 }
176 }
177
178 // Internal re-actor to topology change events.
179 private class InternalTopologyListener implements TopologyListener {
180 @Override
181 public void event(TopologyEvent event) {
182 executorService.execute(new TopologyChangeHandler(event));
183 }
184 }
185
186 // Re-dispatcher of topology change events.
187 private class TopologyChangeHandler implements Runnable {
188
189 private final TopologyEvent event;
190
191 TopologyChangeHandler(TopologyEvent event) {
192 this.event = event;
193 }
194
195 @Override
196 public void run() {
Thomas Vachuska7b652ad2014-10-30 14:10:51 -0700197 // If there is no delegate, why bother? Just bail.
198 if (delegate == null) {
199 return;
200 }
201
Ray Milkey7c44c052014-12-05 10:34:54 -0800202 if (event.reasons() == null || event.reasons().isEmpty()) {
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700203 delegate.triggerCompile(Collections.emptySet(), true);
tom85258ee2014-10-07 00:10:02 -0700204
tom95329eb2014-10-06 08:40:06 -0700205 } else {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800206 Set<Key> toBeRecompiled = new HashSet<>();
tom85258ee2014-10-07 00:10:02 -0700207 boolean recompileOnly = true;
208
209 // Scan through the list of reasons and keep accruing all
210 // intents that need to be recompiled.
tom95329eb2014-10-06 08:40:06 -0700211 for (Event reason : event.reasons()) {
212 if (reason instanceof LinkEvent) {
213 LinkEvent linkEvent = (LinkEvent) reason;
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800214 if (linkEvent.type() == LINK_REMOVED
215 || (linkEvent.type() == LINK_UPDATED &&
216 linkEvent.subject().isDurable())) {
Yuta HIGUCHI56df70f2014-10-28 22:26:15 -0700217 final LinkKey linkKey = linkKey(linkEvent.subject());
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800218 synchronized (intentsByLink) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800219 Set<Key> intentKeys = intentsByLink.get(linkKey);
220 log.debug("recompile triggered by LinkDown {} {}", linkKey, intentKeys);
221 toBeRecompiled.addAll(intentKeys);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800222 }
tom85258ee2014-10-07 00:10:02 -0700223 }
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800224 recompileOnly = recompileOnly &&
225 (linkEvent.type() == LINK_REMOVED ||
226 (linkEvent.type() == LINK_UPDATED &&
227 linkEvent.subject().isDurable()));
tom95329eb2014-10-06 08:40:06 -0700228 }
229 }
tom85258ee2014-10-07 00:10:02 -0700230 delegate.triggerCompile(toBeRecompiled, !recompileOnly);
tom95329eb2014-10-06 08:40:06 -0700231 }
232 }
233 }
234
Ray Milkeye97ede92014-11-20 10:43:12 -0800235 /**
236 * Internal re-actor to resource available events.
237 */
238 private class InternalLinkResourceListener implements LinkResourceListener {
239 @Override
240 public void event(LinkResourceEvent event) {
241 executorService.execute(new ResourceAvailableHandler(event));
242 }
243 }
244
245 /*
246 * Re-dispatcher of resource available events.
247 */
248 private class ResourceAvailableHandler implements Runnable {
249
250 private final LinkResourceEvent event;
251
252 ResourceAvailableHandler(LinkResourceEvent event) {
253 this.event = event;
254 }
255
256 @Override
257 public void run() {
258 // If there is no delegate, why bother? Just bail.
259 if (delegate == null) {
260 return;
261 }
262
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700263 delegate.triggerCompile(Collections.emptySet(), true);
Ray Milkeye97ede92014-11-20 10:43:12 -0800264 }
265 }
266
Brian O'Connor72a034c2014-11-26 18:24:23 -0800267 //TODO consider adding flow rule event tracking
Ray Milkeye97ede92014-11-20 10:43:12 -0800268
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800269 private void updateTrackedResources(ApplicationId appId, boolean track) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800270 if (intentService == null) {
271 log.debug("Intent service is not bound yet");
272 return;
273 }
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800274 intentService.getIntents().forEach(intent -> {
275 if (intent.appId().equals(appId)) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800276 Key key = intent.key();
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800277 Collection<NetworkResource> resources = Lists.newArrayList();
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800278 intentService.getInstallableIntents(key).stream()
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800279 .map(installable -> installable.resources())
280 .forEach(resources::addAll);
281 if (track) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800282 addTrackedResources(key, resources);
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800283 } else {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800284 removeTrackedResources(key, resources);
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800285 }
286 }
287 });
288 }
Brian O'Connorb5dcc512015-03-24 17:28:00 -0700289
290 /*
291 * Re-dispatcher of device and host events.
292 */
293 private class DeviceAvailabilityHandler implements Runnable {
294
295 private final ElementId id;
296 private final boolean available;
297
298 DeviceAvailabilityHandler(ElementId id, boolean available) {
299 this.id = checkNotNull(id);
300 this.available = available;
301 }
302
303 @Override
304 public void run() {
305 // If there is no delegate, why bother? Just bail.
306 if (delegate == null) {
307 return;
308 }
309
310 // TODO should we recompile on available==true?
311 delegate.triggerCompile(intentsByDevice.get(id), available);
312 }
313 }
314
315
316 private class InternalDeviceListener implements DeviceListener {
317 @Override
318 public void event(DeviceEvent event) {
319 DeviceEvent.Type type = event.type();
320 if (type == DeviceEvent.Type.PORT_ADDED ||
321 type == DeviceEvent.Type.PORT_UPDATED ||
322 type == DeviceEvent.Type.PORT_REMOVED) {
323 // skip port events for now
324 return;
325 }
326 DeviceId id = event.subject().id();
327 // TODO we need to check whether AVAILABILITY_CHANGED means up or down
328 boolean available = (type == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
329 type == DeviceEvent.Type.DEVICE_ADDED ||
330 type == DeviceEvent.Type.DEVICE_UPDATED);
331 executorService.execute(new DeviceAvailabilityHandler(id, available));
332
333 }
334 }
335
336 private class InternalHostListener implements HostListener {
337 @Override
338 public void event(HostEvent event) {
339 HostId id = event.subject().id();
340 HostEvent.Type type = event.type();
341 boolean available = (type == HostEvent.Type.HOST_ADDED);
342 executorService.execute(new DeviceAvailabilityHandler(id, available));
343 }
344 }
tom95329eb2014-10-06 08:40:06 -0700345}