blob: 3b1a4a3a239d3c6d3076b29059cfd5a47087b97b [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 */
tombe988312014-09-19 18:38:47 -070016package org.onlab.onos.net.topology.impl;
tomcbff9392014-09-10 00:45:23 -070017
Thomas Vachuska912bdd52014-11-17 11:39:01 -080018import com.google.common.collect.ImmutableList;
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -080019
tomcbff9392014-09-10 00:45:23 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080023import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
tomcbff9392014-09-10 00:45:23 -070025import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070027import org.apache.felix.scr.annotations.Service;
tomcbff9392014-09-10 00:45:23 -070028import org.onlab.onos.event.AbstractEventAccumulator;
29import org.onlab.onos.event.Event;
30import org.onlab.onos.event.EventAccumulator;
31import org.onlab.onos.net.device.DeviceEvent;
32import org.onlab.onos.net.device.DeviceListener;
33import org.onlab.onos.net.device.DeviceService;
34import org.onlab.onos.net.link.LinkEvent;
35import org.onlab.onos.net.link.LinkListener;
36import org.onlab.onos.net.link.LinkService;
37import org.onlab.onos.net.provider.AbstractProvider;
tombe988312014-09-19 18:38:47 -070038import org.onlab.onos.net.topology.DefaultGraphDescription;
tom97937552014-09-11 10:48:42 -070039import org.onlab.onos.net.topology.GraphDescription;
tomcbff9392014-09-10 00:45:23 -070040import org.onlab.onos.net.topology.TopologyProvider;
41import org.onlab.onos.net.topology.TopologyProviderRegistry;
42import org.onlab.onos.net.topology.TopologyProviderService;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080043import org.osgi.service.component.ComponentContext;
tomcbff9392014-09-10 00:45:23 -070044import org.slf4j.Logger;
45
Pavlin Radoslavova0e47542014-10-17 19:22:17 -070046import java.util.Collections;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080047import java.util.Dictionary;
tomcbff9392014-09-10 00:45:23 -070048import java.util.List;
49import java.util.Timer;
50import java.util.concurrent.ExecutorService;
51
Thomas Vachuska912bdd52014-11-17 11:39:01 -080052import static com.google.common.base.Strings.isNullOrEmpty;
tomcbff9392014-09-10 00:45:23 -070053import static java.util.concurrent.Executors.newFixedThreadPool;
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -080054import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID;
tomcbff9392014-09-10 00:45:23 -070055import static org.onlab.onos.net.device.DeviceEvent.Type.*;
56import static org.onlab.util.Tools.namedThreads;
57import static org.slf4j.LoggerFactory.getLogger;
58
59/**
tom578ebdc2014-09-11 11:12:51 -070060 * Default implementation of a network topology provider that feeds off
61 * device and link subsystem events to trigger assembly and computation of
62 * new topology snapshots.
tomcbff9392014-09-10 00:45:23 -070063 */
64@Component(immediate = true)
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070065@Service
tom97937552014-09-11 10:48:42 -070066public class DefaultTopologyProvider extends AbstractProvider
tomcbff9392014-09-10 00:45:23 -070067 implements TopologyProvider {
68
Thomas Vachuska164fa5c2014-12-02 21:59:41 -080069 private static final int MAX_THREADS = 32;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -080070 private static final int DEFAULT_MAX_EVENTS = 1000;
71 private static final int DEFAULT_MAX_IDLE_MS = 10;
72 private static final int DEFAULT_MAX_BATCH_MS = 50;
tomcbff9392014-09-10 00:45:23 -070073
tom025e09f2014-09-15 15:29:24 -070074 // FIXME: Replace with a system-wide timer instance;
75 // TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080076 private static final Timer TIMER = new Timer("topo-event-batching");
tomcbff9392014-09-10 00:45:23 -070077
Thomas Vachuska912bdd52014-11-17 11:39:01 -080078 @Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
79 label = "Maximum number of events to accumulate")
80 private int maxEvents = DEFAULT_MAX_EVENTS;
81
82 @Property(name = "maxIdleMs", intValue = DEFAULT_MAX_IDLE_MS,
83 label = "Maximum number of millis between events")
84 private int maxIdleMs = DEFAULT_MAX_IDLE_MS;
85
86 @Property(name = "maxBatchMs", intValue = DEFAULT_MAX_BATCH_MS,
87 label = "Maximum number of millis for whole batch")
88 private int maxBatchMs = DEFAULT_MAX_BATCH_MS;
89
tomcbff9392014-09-10 00:45:23 -070090 private final Logger log = getLogger(getClass());
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected TopologyProviderRegistry providerRegistry;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected DeviceService deviceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected LinkService linkService;
100
101 private volatile boolean isStarted = false;
102
103 private TopologyProviderService providerService;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700104 private DeviceListener deviceListener = new InternalDeviceListener();
105 private LinkListener linkListener = new InternalLinkListener();
tomcbff9392014-09-10 00:45:23 -0700106
107 private EventAccumulator accumulator;
108 private ExecutorService executor;
109
110 /**
111 * Creates a provider with the supplier identifier.
112 */
tom97937552014-09-11 10:48:42 -0700113 public DefaultTopologyProvider() {
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800114 super(CORE_PROVIDER_ID);
tomcbff9392014-09-10 00:45:23 -0700115 }
116
117 @Activate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800118 public synchronized void activate(ComponentContext context) {
tom578ebdc2014-09-11 11:12:51 -0700119 executor = newFixedThreadPool(MAX_THREADS, namedThreads("topo-build-%d"));
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800120 accumulator = new TopologyChangeAccumulator();
121 logConfig("Configured");
122
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800123 modified(context);
tomcbff9392014-09-10 00:45:23 -0700124
125 providerService = providerRegistry.register(this);
126 deviceService.addListener(deviceListener);
127 linkService.addListener(linkListener);
128
129 isStarted = true;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700130 triggerRecompute();
tomcbff9392014-09-10 00:45:23 -0700131 log.info("Started");
132 }
133
134 @Deactivate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800135 public synchronized void deactivate(ComponentContext context) {
tome52ce702014-09-11 00:12:54 -0700136 isStarted = false;
137
tomcbff9392014-09-10 00:45:23 -0700138 deviceService.removeListener(deviceListener);
139 linkService.removeListener(linkListener);
140 providerRegistry.unregister(this);
141 providerService = null;
142
143 executor.shutdownNow();
144 executor = null;
145
tomcbff9392014-09-10 00:45:23 -0700146 log.info("Stopped");
147 }
148
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800149 @Modified
150 public void modified(ComponentContext context) {
151 if (context == null) {
152 accumulator = new TopologyChangeAccumulator();
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800153 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800154 return;
155 }
156
157 Dictionary properties = context.getProperties();
158 int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
159 try {
160 String s = (String) properties.get("maxEvents");
161 newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s);
162
163 s = (String) properties.get("maxBatchMs");
164 newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s);
165
166 s = (String) properties.get("maxIdleMs");
167 newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800168
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800169 } catch (Exception e) {
170 newMaxEvents = DEFAULT_MAX_EVENTS;
171 newMaxBatchMs = DEFAULT_MAX_BATCH_MS;
172 newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
173 }
174
175 if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
176 maxEvents = newMaxEvents;
177 maxBatchMs = newMaxBatchMs;
178 maxIdleMs = newMaxIdleMs;
179 accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800180 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800181 }
182 }
183
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800184 private void logConfig(String prefix) {
185 log.info("{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
186 prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
187 }
188
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800189
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700190 @Override
191 public void triggerRecompute() {
192 triggerTopologyBuild(Collections.<Event>emptyList());
193 }
194
tomcbff9392014-09-10 00:45:23 -0700195 /**
196 * Triggers assembly of topology data citing the specified events as the
197 * reason.
198 *
199 * @param reasons events which triggered the topology change
200 */
tome52ce702014-09-11 00:12:54 -0700201 private synchronized void triggerTopologyBuild(List<Event> reasons) {
tom97937552014-09-11 10:48:42 -0700202 if (executor != null) {
203 executor.execute(new TopologyBuilderTask(reasons));
204 }
tomcbff9392014-09-10 00:45:23 -0700205 }
206
207 // Builds the topology using the latest device and link information
208 // and citing the specified events as reasons for the change.
209 private void buildTopology(List<Event> reasons) {
tomcbff9392014-09-10 00:45:23 -0700210 if (isStarted) {
tom97937552014-09-11 10:48:42 -0700211 GraphDescription desc =
212 new DefaultGraphDescription(System.nanoTime(),
Yuta HIGUCHIf1f2ac02014-11-26 14:02:22 -0800213 deviceService.getAvailableDevices(),
214 linkService.getActiveLinks());
tomcbff9392014-09-10 00:45:23 -0700215 providerService.topologyChanged(desc, reasons);
216 }
217 }
218
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800219 private void processEvent(Event event) {
220 if (accumulator != null) {
221 accumulator.add(event);
222 } else {
223 triggerTopologyBuild(ImmutableList.of(event));
224 }
225 }
226
tomcbff9392014-09-10 00:45:23 -0700227 // Callback for device events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700228 private class InternalDeviceListener implements DeviceListener {
tomcbff9392014-09-10 00:45:23 -0700229 @Override
230 public void event(DeviceEvent event) {
231 DeviceEvent.Type type = event.type();
232 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
233 type == DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800234 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700235 }
236 }
237 }
238
239 // Callback for link events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700240 private class InternalLinkListener implements LinkListener {
tomcbff9392014-09-10 00:45:23 -0700241 @Override
242 public void event(LinkEvent event) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800243 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700244 }
245 }
246
247 // Event accumulator for paced triggering of topology assembly.
248 private class TopologyChangeAccumulator
249 extends AbstractEventAccumulator implements EventAccumulator {
250
251 TopologyChangeAccumulator() {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800252 super(TIMER, maxEvents, maxBatchMs, maxIdleMs);
tomcbff9392014-09-10 00:45:23 -0700253 }
254
255 @Override
256 public void processEvents(List<Event> events) {
257 triggerTopologyBuild(events);
258 }
259
260 }
261
262 // Task for building topology data in a separate thread.
263 private class TopologyBuilderTask implements Runnable {
264 private final List<Event> reasons;
265
266 public TopologyBuilderTask(List<Event> reasons) {
267 this.reasons = reasons;
268 }
269
270 @Override
271 public void run() {
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700272 try {
273 buildTopology(reasons);
274 } catch (Exception e) {
275 log.warn("Unable to compute topology due to: {}", e.getMessage());
Yuta HIGUCHI22102822014-11-12 23:09:59 -0800276 log.debug("Unable to compute topology", e);
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700277 }
tomcbff9392014-09-10 00:45:23 -0700278 }
279 }
280
281}