blob: 03a7169154732efe2bad5d122c6e6739f0829149 [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.topology.impl;
tomcbff9392014-09-10 00:45:23 -070017
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050018import static com.google.common.base.Strings.isNullOrEmpty;
19import static java.util.concurrent.Executors.newFixedThreadPool;
20import static org.onlab.util.Tools.groupedThreads;
21import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
22import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
23import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
24import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
25import static org.slf4j.LoggerFactory.getLogger;
26
27import java.util.Collections;
28import java.util.Dictionary;
29import java.util.List;
30import java.util.Timer;
31import java.util.concurrent.ExecutorService;
32
tomcbff9392014-09-10 00:45:23 -070033import org.apache.felix.scr.annotations.Activate;
34import org.apache.felix.scr.annotations.Component;
35import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080036import org.apache.felix.scr.annotations.Modified;
37import org.apache.felix.scr.annotations.Property;
tomcbff9392014-09-10 00:45:23 -070038import org.apache.felix.scr.annotations.Reference;
39import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070040import org.apache.felix.scr.annotations.Service;
Thomas Vachuskaecb63c52015-02-19 10:03:48 -080041import org.onlab.util.AbstractAccumulator;
42import org.onlab.util.Accumulator;
Thomas Vachuska6d697f12015-03-08 20:59:50 -070043import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.event.Event;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.device.DeviceEvent;
46import org.onosproject.net.device.DeviceListener;
47import org.onosproject.net.device.DeviceService;
48import org.onosproject.net.link.LinkEvent;
49import org.onosproject.net.link.LinkListener;
50import org.onosproject.net.link.LinkService;
51import org.onosproject.net.provider.AbstractProvider;
52import org.onosproject.net.topology.DefaultGraphDescription;
53import org.onosproject.net.topology.GraphDescription;
54import org.onosproject.net.topology.TopologyProvider;
55import org.onosproject.net.topology.TopologyProviderRegistry;
56import org.onosproject.net.topology.TopologyProviderService;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080057import org.osgi.service.component.ComponentContext;
tomcbff9392014-09-10 00:45:23 -070058import org.slf4j.Logger;
59
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050060import com.google.common.collect.ImmutableList;
tomcbff9392014-09-10 00:45:23 -070061
62/**
Thomas Vachuska6d697f12015-03-08 20:59:50 -070063 * Default implementation of a network topology provider that feeds off
64 * device and link subsystem events to trigger assembly and computation of
65 * new topology snapshots.
tomcbff9392014-09-10 00:45:23 -070066 */
67@Component(immediate = true)
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070068@Service
Thomas Vachuska6d697f12015-03-08 20:59:50 -070069public class DefaultTopologyProvider extends AbstractProvider
70 implements TopologyProvider {
tomcbff9392014-09-10 00:45:23 -070071
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080072 private static final int MAX_THREADS = 8;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -080073 private static final int DEFAULT_MAX_EVENTS = 1000;
74 private static final int DEFAULT_MAX_IDLE_MS = 10;
75 private static final int DEFAULT_MAX_BATCH_MS = 50;
tomcbff9392014-09-10 00:45:23 -070076
tom025e09f2014-09-15 15:29:24 -070077 // FIXME: Replace with a system-wide timer instance;
Thomas Vachuska6d697f12015-03-08 20:59:50 -070078 // TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080079 private static final Timer TIMER = new Timer("onos-topo-event-batching");
tomcbff9392014-09-10 00:45:23 -070080
Thomas Vachuska912bdd52014-11-17 11:39:01 -080081 @Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
82 label = "Maximum number of events to accumulate")
83 private int maxEvents = DEFAULT_MAX_EVENTS;
84
85 @Property(name = "maxIdleMs", intValue = DEFAULT_MAX_IDLE_MS,
86 label = "Maximum number of millis between events")
87 private int maxIdleMs = DEFAULT_MAX_IDLE_MS;
88
89 @Property(name = "maxBatchMs", intValue = DEFAULT_MAX_BATCH_MS,
90 label = "Maximum number of millis for whole batch")
91 private int maxBatchMs = DEFAULT_MAX_BATCH_MS;
92
tomcbff9392014-09-10 00:45:23 -070093 private final Logger log = getLogger(getClass());
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected TopologyProviderRegistry providerRegistry;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected DeviceService deviceService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected LinkService linkService;
103
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected ComponentConfigService cfgService;
106
tomcbff9392014-09-10 00:45:23 -0700107 private volatile boolean isStarted = false;
108
109 private TopologyProviderService providerService;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500110 private final DeviceListener deviceListener = new InternalDeviceListener();
111 private final LinkListener linkListener = new InternalLinkListener();
tomcbff9392014-09-10 00:45:23 -0700112
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800113 private Accumulator<Event> accumulator;
tomcbff9392014-09-10 00:45:23 -0700114 private ExecutorService executor;
115
116 /**
117 * Creates a provider with the supplier identifier.
118 */
tom97937552014-09-11 10:48:42 -0700119 public DefaultTopologyProvider() {
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800120 super(CORE_PROVIDER_ID);
tomcbff9392014-09-10 00:45:23 -0700121 }
122
123 @Activate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800124 public synchronized void activate(ComponentContext context) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700125 cfgService.registerProperties(DefaultTopologyProvider.class);
126 executor = newFixedThreadPool(MAX_THREADS, groupedThreads("onos/topo", "build-%d"));
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800127 accumulator = new TopologyChangeAccumulator();
128 logConfig("Configured");
129
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800130 modified(context);
tomcbff9392014-09-10 00:45:23 -0700131
132 providerService = providerRegistry.register(this);
133 deviceService.addListener(deviceListener);
134 linkService.addListener(linkListener);
135
136 isStarted = true;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700137 triggerRecompute();
tomcbff9392014-09-10 00:45:23 -0700138 log.info("Started");
139 }
140
141 @Deactivate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800142 public synchronized void deactivate(ComponentContext context) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700143 cfgService.unregisterProperties(DefaultTopologyProvider.class, false);
tome52ce702014-09-11 00:12:54 -0700144 isStarted = false;
145
tomcbff9392014-09-10 00:45:23 -0700146 deviceService.removeListener(deviceListener);
147 linkService.removeListener(linkListener);
148 providerRegistry.unregister(this);
149 providerService = null;
150
151 executor.shutdownNow();
152 executor = null;
153
tomcbff9392014-09-10 00:45:23 -0700154 log.info("Stopped");
155 }
156
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800157 @Modified
158 public void modified(ComponentContext context) {
159 if (context == null) {
160 accumulator = new TopologyChangeAccumulator();
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800161 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800162 return;
163 }
164
165 Dictionary properties = context.getProperties();
166 int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
167 try {
168 String s = (String) properties.get("maxEvents");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800169 newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800170
171 s = (String) properties.get("maxBatchMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800172 newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800173
174 s = (String) properties.get("maxIdleMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800175 newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s.trim());
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800176
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800177 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800178 newMaxEvents = DEFAULT_MAX_EVENTS;
179 newMaxBatchMs = DEFAULT_MAX_BATCH_MS;
180 newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
181 }
182
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700183 if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800184 maxEvents = newMaxEvents;
185 maxBatchMs = newMaxBatchMs;
186 maxIdleMs = newMaxIdleMs;
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700187 accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800188 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800189 }
190 }
191
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800192 private void logConfig(String prefix) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700193 log.info("{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
194 prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800195 }
196
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700197
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700198 @Override
199 public void triggerRecompute() {
200 triggerTopologyBuild(Collections.<Event>emptyList());
201 }
202
tomcbff9392014-09-10 00:45:23 -0700203 /**
204 * Triggers assembly of topology data citing the specified events as the
205 * reason.
206 *
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700207 * @param reasons events which triggered the topology change
tomcbff9392014-09-10 00:45:23 -0700208 */
tome52ce702014-09-11 00:12:54 -0700209 private synchronized void triggerTopologyBuild(List<Event> reasons) {
tom97937552014-09-11 10:48:42 -0700210 if (executor != null) {
211 executor.execute(new TopologyBuilderTask(reasons));
212 }
tomcbff9392014-09-10 00:45:23 -0700213 }
214
215 // Builds the topology using the latest device and link information
216 // and citing the specified events as reasons for the change.
217 private void buildTopology(List<Event> reasons) {
tomcbff9392014-09-10 00:45:23 -0700218 if (isStarted) {
tom97937552014-09-11 10:48:42 -0700219 GraphDescription desc =
220 new DefaultGraphDescription(System.nanoTime(),
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700221 System.currentTimeMillis(),
222 deviceService.getAvailableDevices(),
223 linkService.getActiveLinks());
tomcbff9392014-09-10 00:45:23 -0700224 providerService.topologyChanged(desc, reasons);
225 }
226 }
227
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800228 private void processEvent(Event event) {
229 if (accumulator != null) {
230 accumulator.add(event);
231 } else {
232 triggerTopologyBuild(ImmutableList.of(event));
233 }
234 }
235
tomcbff9392014-09-10 00:45:23 -0700236 // Callback for device events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700237 private class InternalDeviceListener implements DeviceListener {
tomcbff9392014-09-10 00:45:23 -0700238 @Override
239 public void event(DeviceEvent event) {
240 DeviceEvent.Type type = event.type();
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700241 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
242 type == DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800243 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700244 }
245 }
246 }
247
248 // Callback for link events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700249 private class InternalLinkListener implements LinkListener {
tomcbff9392014-09-10 00:45:23 -0700250 @Override
251 public void event(LinkEvent event) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800252 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700253 }
254 }
255
256 // Event accumulator for paced triggering of topology assembly.
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800257 private class TopologyChangeAccumulator extends AbstractAccumulator<Event> {
tomcbff9392014-09-10 00:45:23 -0700258 TopologyChangeAccumulator() {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800259 super(TIMER, maxEvents, maxBatchMs, maxIdleMs);
tomcbff9392014-09-10 00:45:23 -0700260 }
261
262 @Override
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800263 public void processItems(List<Event> items) {
264 triggerTopologyBuild(items);
tomcbff9392014-09-10 00:45:23 -0700265 }
tomcbff9392014-09-10 00:45:23 -0700266 }
267
268 // Task for building topology data in a separate thread.
269 private class TopologyBuilderTask implements Runnable {
270 private final List<Event> reasons;
271
272 public TopologyBuilderTask(List<Event> reasons) {
273 this.reasons = reasons;
274 }
275
276 @Override
277 public void run() {
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700278 try {
279 buildTopology(reasons);
280 } catch (Exception e) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700281 log.warn("Unable to compute topology due to: {}", e.getMessage());
Yuta HIGUCHI22102822014-11-12 23:09:59 -0800282 log.debug("Unable to compute topology", e);
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700283 }
tomcbff9392014-09-10 00:45:23 -0700284 }
285 }
286
287}