blob: 83a3ccb77d2bae4fb49d1fe5d458f1bfcda277af [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'Connor86f6f7f2014-12-01 17:02:45 -080080 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 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
111 @Override
112 public void setDelegate(TopologyChangeDelegate delegate) {
113 checkNotNull(delegate, "Delegate cannot be null");
114 checkArgument(this.delegate == null || this.delegate == delegate,
115 "Another delegate already set");
116 this.delegate = delegate;
117 }
118
119 @Override
120 public void unsetDelegate(TopologyChangeDelegate delegate) {
121 checkArgument(this.delegate == delegate, "Not the current delegate");
122 this.delegate = null;
123 }
124
125 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700126 public void addTrackedResources(IntentId intentId,
127 Collection<NetworkResource> resources) {
128 for (NetworkResource resource : resources) {
129 if (resource instanceof Link) {
130 intentsByLink.put(linkKey((Link) resource), intentId);
131 }
tom95329eb2014-10-06 08:40:06 -0700132 }
133 }
134
135 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700136 public void removeTrackedResources(IntentId intentId,
137 Collection<NetworkResource> resources) {
138 for (NetworkResource resource : resources) {
139 if (resource instanceof Link) {
140 intentsByLink.remove(linkKey((Link) resource), intentId);
141 }
tom95329eb2014-10-06 08:40:06 -0700142 }
143 }
144
145 // Internal re-actor to topology change events.
146 private class InternalTopologyListener implements TopologyListener {
147 @Override
148 public void event(TopologyEvent event) {
149 executorService.execute(new TopologyChangeHandler(event));
150 }
151 }
152
153 // Re-dispatcher of topology change events.
154 private class TopologyChangeHandler implements Runnable {
155
156 private final TopologyEvent event;
157
158 TopologyChangeHandler(TopologyEvent event) {
159 this.event = event;
160 }
161
162 @Override
163 public void run() {
Thomas Vachuska7b652ad2014-10-30 14:10:51 -0700164 // If there is no delegate, why bother? Just bail.
165 if (delegate == null) {
166 return;
167 }
168
tom95329eb2014-10-06 08:40:06 -0700169 if (event.reasons() == null) {
tom1dd08e42014-10-07 11:40:00 -0700170 delegate.triggerCompile(new HashSet<IntentId>(), true);
tom85258ee2014-10-07 00:10:02 -0700171
tom95329eb2014-10-06 08:40:06 -0700172 } else {
tom85258ee2014-10-07 00:10:02 -0700173 Set<IntentId> toBeRecompiled = new HashSet<>();
174 boolean recompileOnly = true;
175
176 // Scan through the list of reasons and keep accruing all
177 // intents that need to be recompiled.
tom95329eb2014-10-06 08:40:06 -0700178 for (Event reason : event.reasons()) {
179 if (reason instanceof LinkEvent) {
180 LinkEvent linkEvent = (LinkEvent) reason;
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800181 if (linkEvent.type() == LINK_REMOVED
182 || (linkEvent.type() == LINK_UPDATED &&
183 linkEvent.subject().isDurable())) {
Yuta HIGUCHI56df70f2014-10-28 22:26:15 -0700184 final LinkKey linkKey = linkKey(linkEvent.subject());
185 Set<IntentId> intentIds = intentsByLink.get(linkKey);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800186 synchronized (intentsByLink) {
187 log.debug("recompile triggered by LinkDown {} {}", linkKey, intentIds);
188 toBeRecompiled.addAll(intentIds);
189 }
tom85258ee2014-10-07 00:10:02 -0700190 }
Praseed Balakrishnan00dd1f92014-11-19 17:12:36 -0800191 recompileOnly = recompileOnly &&
192 (linkEvent.type() == LINK_REMOVED ||
193 (linkEvent.type() == LINK_UPDATED &&
194 linkEvent.subject().isDurable()));
tom95329eb2014-10-06 08:40:06 -0700195 }
196 }
tom85258ee2014-10-07 00:10:02 -0700197
198 delegate.triggerCompile(toBeRecompiled, !recompileOnly);
tom95329eb2014-10-06 08:40:06 -0700199 }
200 }
201 }
202
Ray Milkeye97ede92014-11-20 10:43:12 -0800203 /**
204 * Internal re-actor to resource available events.
205 */
206 private class InternalLinkResourceListener implements LinkResourceListener {
207 @Override
208 public void event(LinkResourceEvent event) {
209 executorService.execute(new ResourceAvailableHandler(event));
210 }
211 }
212
213 /*
214 * Re-dispatcher of resource available events.
215 */
216 private class ResourceAvailableHandler implements Runnable {
217
218 private final LinkResourceEvent event;
219
220 ResourceAvailableHandler(LinkResourceEvent event) {
221 this.event = event;
222 }
223
224 @Override
225 public void run() {
226 // If there is no delegate, why bother? Just bail.
227 if (delegate == null) {
228 return;
229 }
230
231 delegate.triggerCompile(new HashSet<>(), true);
232 }
233 }
234
Brian O'Connor72a034c2014-11-26 18:24:23 -0800235 //TODO consider adding flow rule event tracking
Ray Milkeye97ede92014-11-20 10:43:12 -0800236
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800237 private void updateTrackedResources(ApplicationId appId, boolean track) {
238 intentService.getIntents().forEach(intent -> {
239 if (intent.appId().equals(appId)) {
240 IntentId id = intent.id();
241 Collection<NetworkResource> resources = Lists.newArrayList();
242 intentService.getInstallableIntents(id).stream()
243 .map(installable -> installable.resources())
244 .forEach(resources::addAll);
245 if (track) {
246 addTrackedResources(id, resources);
247 } else {
248 removeTrackedResources(id, resources);
249 }
250 }
251 });
252 }
253
254 private class LeadershipListener implements IntentBatchListener {
255 @Override
256 public void event(IntentBatchLeaderEvent event) {
257 log.debug("leadership event: {}", event);
258 ApplicationId appId = event.subject();
259 switch (event.type()) {
260 case ELECTED:
261 updateTrackedResources(appId, true);
262 break;
263 case BOOTED:
264 updateTrackedResources(appId, false);
265 break;
266 default:
267 break;
268 }
269 }
270 }
tom95329eb2014-10-06 08:40:06 -0700271}