blob: 418082722223c08c8f52af82417c2959adb8cbcc [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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;
29import org.onosproject.net.Link;
30import org.onosproject.net.LinkKey;
31import org.onosproject.net.NetworkResource;
32import org.onosproject.net.intent.IntentBatchLeaderEvent;
33import org.onosproject.net.intent.IntentBatchListener;
34import org.onosproject.net.intent.IntentBatchService;
35import org.onosproject.net.intent.IntentId;
36import org.onosproject.net.intent.IntentService;
37import org.onosproject.net.link.LinkEvent;
38import org.onosproject.net.resource.LinkResourceEvent;
39import org.onosproject.net.resource.LinkResourceListener;
40import org.onosproject.net.resource.LinkResourceService;
41import org.onosproject.net.topology.TopologyEvent;
42import org.onosproject.net.topology.TopologyListener;
43import org.onosproject.net.topology.TopologyService;
tom95329eb2014-10-06 08:40:06 -070044import org.slf4j.Logger;
45
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080046import java.util.Collection;
47import java.util.HashSet;
48import java.util.Set;
49import java.util.concurrent.ExecutorService;
tom95329eb2014-10-06 08:40:06 -070050
51import static com.google.common.base.Preconditions.checkArgument;
52import static com.google.common.base.Preconditions.checkNotNull;
53import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
54import static java.util.concurrent.Executors.newSingleThreadExecutor;
Brian O'Connorabafb502014-12-02 22:26:20 -080055import static org.onosproject.net.LinkKey.linkKey;
56import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
57import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
tom95329eb2014-10-06 08:40:06 -070058import static org.onlab.util.Tools.namedThreads;
59import static org.slf4j.LoggerFactory.getLogger;
60
61/**
62 * Entity responsible for tracking installed flows and for monitoring topology
63 * events to determine what flows are affected by topology changes.
64 */
Ray Milkeye97ede92014-11-20 10:43:12 -080065@Component(immediate = true)
tom95329eb2014-10-06 08:40:06 -070066@Service
tom85258ee2014-10-07 00:10:02 -070067public class ObjectiveTracker implements ObjectiveTrackerService {
tom95329eb2014-10-06 08:40:06 -070068
69 private final Logger log = getLogger(getClass());
70
71 private final SetMultimap<LinkKey, IntentId> intentsByLink =
72 synchronizedSetMultimap(HashMultimap.<LinkKey, IntentId>create());
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected TopologyService topologyService;
76
Ray Milkeye97ede92014-11-20 10:43:12 -080077 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected LinkResourceService resourceManager;
79
Brian O'Connor5d55ed42014-12-01 18:27:47 -080080 @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080081 protected IntentService intentService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected IntentBatchService batchService;
85
tom95329eb2014-10-06 08:40:06 -070086 private ExecutorService executorService =
87 newSingleThreadExecutor(namedThreads("onos-flowtracker"));
88
89 private TopologyListener listener = new InternalTopologyListener();
Ray Milkeye97ede92014-11-20 10:43:12 -080090 private LinkResourceListener linkResourceListener =
91 new InternalLinkResourceListener();
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080092 private final LeadershipListener leaderListener = new LeadershipListener();
tom95329eb2014-10-06 08:40:06 -070093 private TopologyChangeDelegate delegate;
94
95 @Activate
96 public void activate() {
97 topologyService.addListener(listener);
Ray Milkeye97ede92014-11-20 10:43:12 -080098 resourceManager.addListener(linkResourceListener);
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080099 batchService.addListener(leaderListener);
tom95329eb2014-10-06 08:40:06 -0700100 log.info("Started");
101 }
102
103 @Deactivate
104 public void deactivate() {
105 topologyService.removeListener(listener);
Ray Milkeye97ede92014-11-20 10:43:12 -0800106 resourceManager.removeListener(linkResourceListener);
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800107 batchService.removeListener(leaderListener);
tom95329eb2014-10-06 08:40:06 -0700108 log.info("Stopped");
109 }
110
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800111 protected void bindIntentService(IntentService service) {
112 if (intentService == null) {
113 intentService = service;
114 }
115 }
116
117 protected void unbindIntentService(IntentService service) {
118 if (intentService == service) {
119 intentService = null;
120 }
121 }
122
tom95329eb2014-10-06 08:40:06 -0700123 @Override
124 public void setDelegate(TopologyChangeDelegate delegate) {
125 checkNotNull(delegate, "Delegate cannot be null");
126 checkArgument(this.delegate == null || this.delegate == delegate,
127 "Another delegate already set");
128 this.delegate = delegate;
129 }
130
131 @Override
132 public void unsetDelegate(TopologyChangeDelegate delegate) {
133 checkArgument(this.delegate == delegate, "Not the current delegate");
134 this.delegate = null;
135 }
136
137 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700138 public void addTrackedResources(IntentId intentId,
139 Collection<NetworkResource> resources) {
140 for (NetworkResource resource : resources) {
141 if (resource instanceof Link) {
142 intentsByLink.put(linkKey((Link) resource), intentId);
143 }
tom95329eb2014-10-06 08:40:06 -0700144 }
145 }
146
147 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700148 public void removeTrackedResources(IntentId intentId,
149 Collection<NetworkResource> resources) {
150 for (NetworkResource resource : resources) {
151 if (resource instanceof Link) {
152 intentsByLink.remove(linkKey((Link) resource), intentId);
153 }
tom95329eb2014-10-06 08:40:06 -0700154 }
155 }
156
157 // Internal re-actor to topology change events.
158 private class InternalTopologyListener implements TopologyListener {
159 @Override
160 public void event(TopologyEvent event) {
161 executorService.execute(new TopologyChangeHandler(event));
162 }
163 }
164
165 // Re-dispatcher of topology change events.
166 private class TopologyChangeHandler implements Runnable {
167
168 private final TopologyEvent event;
169
170 TopologyChangeHandler(TopologyEvent event) {
171 this.event = event;
172 }
173
174 @Override
175 public void run() {
Thomas Vachuska7b652ad2014-10-30 14:10:51 -0700176 // If there is no delegate, why bother? Just bail.
177 if (delegate == null) {
178 return;
179 }
180
tom95329eb2014-10-06 08:40:06 -0700181 if (event.reasons() == null) {
tom1dd08e42014-10-07 11:40:00 -0700182 delegate.triggerCompile(new HashSet<IntentId>(), true);
tom85258ee2014-10-07 00:10:02 -0700183
tom95329eb2014-10-06 08:40:06 -0700184 } else {
tom85258ee2014-10-07 00:10:02 -0700185 Set<IntentId> toBeRecompiled = new HashSet<>();
186 boolean recompileOnly = true;
187
188 // Scan through the list of reasons and keep accruing all
189 // intents that need to be recompiled.
tom95329eb2014-10-06 08:40:06 -0700190 for (Event reason : event.reasons()) {
191 if (reason instanceof LinkEvent) {
192 LinkEvent linkEvent = (LinkEvent) reason;
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800193 if (linkEvent.type() == LINK_REMOVED
194 || (linkEvent.type() == LINK_UPDATED &&
195 linkEvent.subject().isDurable())) {
Yuta HIGUCHI56df70f2014-10-28 22:26:15 -0700196 final LinkKey linkKey = linkKey(linkEvent.subject());
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800197 synchronized (intentsByLink) {
Brian O'Connor44008532014-12-04 16:41:36 -0800198 Set<IntentId> intentIds = intentsByLink.get(linkKey);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800199 log.debug("recompile triggered by LinkDown {} {}", linkKey, intentIds);
200 toBeRecompiled.addAll(intentIds);
201 }
tom85258ee2014-10-07 00:10:02 -0700202 }
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800203 recompileOnly = recompileOnly &&
204 (linkEvent.type() == LINK_REMOVED ||
205 (linkEvent.type() == LINK_UPDATED &&
206 linkEvent.subject().isDurable()));
tom95329eb2014-10-06 08:40:06 -0700207 }
208 }
tom85258ee2014-10-07 00:10:02 -0700209 delegate.triggerCompile(toBeRecompiled, !recompileOnly);
tom95329eb2014-10-06 08:40:06 -0700210 }
211 }
212 }
213
Ray Milkeye97ede92014-11-20 10:43:12 -0800214 /**
215 * Internal re-actor to resource available events.
216 */
217 private class InternalLinkResourceListener implements LinkResourceListener {
218 @Override
219 public void event(LinkResourceEvent event) {
220 executorService.execute(new ResourceAvailableHandler(event));
221 }
222 }
223
224 /*
225 * Re-dispatcher of resource available events.
226 */
227 private class ResourceAvailableHandler implements Runnable {
228
229 private final LinkResourceEvent event;
230
231 ResourceAvailableHandler(LinkResourceEvent event) {
232 this.event = event;
233 }
234
235 @Override
236 public void run() {
237 // If there is no delegate, why bother? Just bail.
238 if (delegate == null) {
239 return;
240 }
241
242 delegate.triggerCompile(new HashSet<>(), true);
243 }
244 }
245
Brian O'Connor72a034c2014-11-26 18:24:23 -0800246 //TODO consider adding flow rule event tracking
Ray Milkeye97ede92014-11-20 10:43:12 -0800247
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800248 private void updateTrackedResources(ApplicationId appId, boolean track) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800249 if (intentService == null) {
250 log.debug("Intent service is not bound yet");
251 return;
252 }
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800253 intentService.getIntents().forEach(intent -> {
254 if (intent.appId().equals(appId)) {
255 IntentId id = intent.id();
256 Collection<NetworkResource> resources = Lists.newArrayList();
257 intentService.getInstallableIntents(id).stream()
258 .map(installable -> installable.resources())
259 .forEach(resources::addAll);
260 if (track) {
261 addTrackedResources(id, resources);
262 } else {
263 removeTrackedResources(id, resources);
264 }
265 }
266 });
267 }
268
269 private class LeadershipListener implements IntentBatchListener {
270 @Override
271 public void event(IntentBatchLeaderEvent event) {
272 log.debug("leadership event: {}", event);
273 ApplicationId appId = event.subject();
274 switch (event.type()) {
275 case ELECTED:
276 updateTrackedResources(appId, true);
277 break;
278 case BOOTED:
279 updateTrackedResources(appId, false);
280 break;
281 default:
282 break;
283 }
284 }
285 }
tom95329eb2014-10-06 08:40:06 -0700286}