blob: ec22bd29e76a7df7ea3f7e88b7743a9e9593e98e [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 */
tom95329eb2014-10-06 08:40:06 -070016package org.onlab.onos.net.intent.impl;
17
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'Connor86f6f7f2014-12-01 17:02:45 -080027import org.onlab.onos.core.ApplicationId;
tom95329eb2014-10-06 08:40:06 -070028import org.onlab.onos.event.Event;
29import org.onlab.onos.net.Link;
30import org.onlab.onos.net.LinkKey;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070031import org.onlab.onos.net.NetworkResource;
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080032import org.onlab.onos.net.intent.IntentBatchLeaderEvent;
33import org.onlab.onos.net.intent.IntentBatchListener;
34import org.onlab.onos.net.intent.IntentBatchService;
tom95329eb2014-10-06 08:40:06 -070035import org.onlab.onos.net.intent.IntentId;
Brian O'Connor86f6f7f2014-12-01 17:02:45 -080036import org.onlab.onos.net.intent.IntentService;
tom95329eb2014-10-06 08:40:06 -070037import org.onlab.onos.net.link.LinkEvent;
Ray Milkeye97ede92014-11-20 10:43:12 -080038import org.onlab.onos.net.resource.LinkResourceEvent;
39import org.onlab.onos.net.resource.LinkResourceListener;
40import org.onlab.onos.net.resource.LinkResourceService;
tom95329eb2014-10-06 08:40:06 -070041import org.onlab.onos.net.topology.TopologyEvent;
42import org.onlab.onos.net.topology.TopologyListener;
43import org.onlab.onos.net.topology.TopologyService;
44import 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;
Yuta HIGUCHI18ab8a92014-10-13 11:16:19 -070055import static org.onlab.onos.net.LinkKey.linkKey;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070056import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -080057import static org.onlab.onos.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());
197 Set<IntentId> intentIds = intentsByLink.get(linkKey);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800198 synchronized (intentsByLink) {
199 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
210 delegate.triggerCompile(toBeRecompiled, !recompileOnly);
tom95329eb2014-10-06 08:40:06 -0700211 }
212 }
213 }
214
Ray Milkeye97ede92014-11-20 10:43:12 -0800215 /**
216 * Internal re-actor to resource available events.
217 */
218 private class InternalLinkResourceListener implements LinkResourceListener {
219 @Override
220 public void event(LinkResourceEvent event) {
221 executorService.execute(new ResourceAvailableHandler(event));
222 }
223 }
224
225 /*
226 * Re-dispatcher of resource available events.
227 */
228 private class ResourceAvailableHandler implements Runnable {
229
230 private final LinkResourceEvent event;
231
232 ResourceAvailableHandler(LinkResourceEvent event) {
233 this.event = event;
234 }
235
236 @Override
237 public void run() {
238 // If there is no delegate, why bother? Just bail.
239 if (delegate == null) {
240 return;
241 }
242
243 delegate.triggerCompile(new HashSet<>(), true);
244 }
245 }
246
Brian O'Connor72a034c2014-11-26 18:24:23 -0800247 //TODO consider adding flow rule event tracking
Ray Milkeye97ede92014-11-20 10:43:12 -0800248
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800249 private void updateTrackedResources(ApplicationId appId, boolean track) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800250 if (intentService == null) {
251 log.debug("Intent service is not bound yet");
252 return;
253 }
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800254 intentService.getIntents().forEach(intent -> {
255 if (intent.appId().equals(appId)) {
256 IntentId id = intent.id();
257 Collection<NetworkResource> resources = Lists.newArrayList();
258 intentService.getInstallableIntents(id).stream()
259 .map(installable -> installable.resources())
260 .forEach(resources::addAll);
261 if (track) {
262 addTrackedResources(id, resources);
263 } else {
264 removeTrackedResources(id, resources);
265 }
266 }
267 });
268 }
269
270 private class LeadershipListener implements IntentBatchListener {
271 @Override
272 public void event(IntentBatchLeaderEvent event) {
273 log.debug("leadership event: {}", event);
274 ApplicationId appId = event.subject();
275 switch (event.type()) {
276 case ELECTED:
277 updateTrackedResources(appId, true);
278 break;
279 case BOOTED:
280 updateTrackedResources(appId, false);
281 break;
282 default:
283 break;
284 }
285 }
286 }
tom95329eb2014-10-06 08:40:06 -0700287}