blob: 0c33512bc2902b1fb6b415d386f82596ce5453b8 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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
Ray Milkeyd84f89b2018-08-17 14:54:17 -070018import com.google.common.collect.ImmutableList;
Thomas Vachuskaecb63c52015-02-19 10:03:48 -080019import org.onlab.util.AbstractAccumulator;
20import org.onlab.util.Accumulator;
Thomas Vachuska6d697f12015-03-08 20:59:50 -070021import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080022import org.onosproject.event.Event;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.net.device.DeviceEvent;
24import org.onosproject.net.device.DeviceListener;
25import org.onosproject.net.device.DeviceService;
26import org.onosproject.net.link.LinkEvent;
27import org.onosproject.net.link.LinkListener;
28import org.onosproject.net.link.LinkService;
29import org.onosproject.net.provider.AbstractProvider;
30import org.onosproject.net.topology.DefaultGraphDescription;
31import org.onosproject.net.topology.GraphDescription;
32import org.onosproject.net.topology.TopologyProvider;
33import org.onosproject.net.topology.TopologyProviderRegistry;
34import org.onosproject.net.topology.TopologyProviderService;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080035import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070036import org.osgi.service.component.annotations.Activate;
37import org.osgi.service.component.annotations.Component;
38import org.osgi.service.component.annotations.Deactivate;
39import org.osgi.service.component.annotations.Modified;
40import org.osgi.service.component.annotations.Reference;
41import org.osgi.service.component.annotations.ReferenceCardinality;
tomcbff9392014-09-10 00:45:23 -070042import org.slf4j.Logger;
43
Ray Milkeyd84f89b2018-08-17 14:54:17 -070044import java.util.Collections;
45import java.util.Dictionary;
46import java.util.List;
47import java.util.Timer;
48import java.util.concurrent.ExecutorService;
49
50import static com.google.common.base.Strings.isNullOrEmpty;
51import static java.util.concurrent.Executors.newFixedThreadPool;
52import static org.onlab.util.Tools.get;
53import static org.onlab.util.Tools.groupedThreads;
54import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
Ray Milkeyd04e2272018-10-16 18:20:18 -070055import static org.onosproject.net.OsgiPropertyConstants.DTP_MAX_BATCH_MS;
56import static org.onosproject.net.OsgiPropertyConstants.DTP_MAX_BATCH_MS_DEFAULT;
57import static org.onosproject.net.OsgiPropertyConstants.DTP_MAX_EVENTS;
58import static org.onosproject.net.OsgiPropertyConstants.DTP_MAX_EVENTS_DEFAULT;
59import static org.onosproject.net.OsgiPropertyConstants.DTP_MAX_IDLE_MS;
60import static org.onosproject.net.OsgiPropertyConstants.DTP_MAX_IDLE_MS_DEFAULT;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070061import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
62import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
63import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
64import static org.slf4j.LoggerFactory.getLogger;
tomcbff9392014-09-10 00:45:23 -070065
66/**
Thomas Vachuska6d697f12015-03-08 20:59:50 -070067 * Default implementation of a network topology provider that feeds off
68 * device and link subsystem events to trigger assembly and computation of
69 * new topology snapshots.
tomcbff9392014-09-10 00:45:23 -070070 */
Ray Milkeyd04e2272018-10-16 18:20:18 -070071@Component(
72 immediate = true,
73 service = TopologyProvider.class,
74 property = {
75 DTP_MAX_EVENTS + "=" + DTP_MAX_EVENTS_DEFAULT,
76 DTP_MAX_IDLE_MS + "=" + DTP_MAX_IDLE_MS_DEFAULT,
77 DTP_MAX_BATCH_MS + "=" + DTP_MAX_BATCH_MS_DEFAULT
78 }
79)
Thomas Vachuska6d697f12015-03-08 20:59:50 -070080public class DefaultTopologyProvider extends AbstractProvider
81 implements TopologyProvider {
tomcbff9392014-09-10 00:45:23 -070082
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080083 private static final int MAX_THREADS = 8;
tomcbff9392014-09-10 00:45:23 -070084
tom025e09f2014-09-15 15:29:24 -070085 // FIXME: Replace with a system-wide timer instance;
Thomas Vachuska6d697f12015-03-08 20:59:50 -070086 // 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 -080087 private static final Timer TIMER = new Timer("onos-topo-event-batching");
tomcbff9392014-09-10 00:45:23 -070088
Ray Milkeyd84f89b2018-08-17 14:54:17 -070089 //@Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
90 // label = "Maximum number of events to accumulate")
Ray Milkeyd04e2272018-10-16 18:20:18 -070091 private int maxEvents = DTP_MAX_EVENTS_DEFAULT;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080092
Ray Milkeyd84f89b2018-08-17 14:54:17 -070093 //@Property(name = "maxIdleMs", intValue = DEFAULT_MAX_IDLE_MS,
94 // label = "Maximum number of millis between events")
Ray Milkeyd04e2272018-10-16 18:20:18 -070095 private int maxIdleMs = DTP_MAX_IDLE_MS_DEFAULT;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080096
Ray Milkeyd84f89b2018-08-17 14:54:17 -070097 //@Property(name = "maxBatchMs", intValue = DEFAULT_MAX_BATCH_MS,
98 // label = "Maximum number of millis for whole batch")
Ray Milkeyd04e2272018-10-16 18:20:18 -070099 private int maxBatchMs = DTP_MAX_BATCH_MS_DEFAULT;
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800100
tomcbff9392014-09-10 00:45:23 -0700101 private final Logger log = getLogger(getClass());
102
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tomcbff9392014-09-10 00:45:23 -0700104 protected TopologyProviderRegistry providerRegistry;
105
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tomcbff9392014-09-10 00:45:23 -0700107 protected DeviceService deviceService;
108
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tomcbff9392014-09-10 00:45:23 -0700110 protected LinkService linkService;
111
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700113 protected ComponentConfigService cfgService;
114
tomcbff9392014-09-10 00:45:23 -0700115 private volatile boolean isStarted = false;
116
117 private TopologyProviderService providerService;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500118 private final DeviceListener deviceListener = new InternalDeviceListener();
119 private final LinkListener linkListener = new InternalLinkListener();
tomcbff9392014-09-10 00:45:23 -0700120
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800121 private Accumulator<Event> accumulator;
tomcbff9392014-09-10 00:45:23 -0700122 private ExecutorService executor;
123
124 /**
125 * Creates a provider with the supplier identifier.
126 */
tom97937552014-09-11 10:48:42 -0700127 public DefaultTopologyProvider() {
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800128 super(CORE_PROVIDER_ID);
tomcbff9392014-09-10 00:45:23 -0700129 }
130
131 @Activate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800132 public synchronized void activate(ComponentContext context) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700133 cfgService.registerProperties(DefaultTopologyProvider.class);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700134 executor = newFixedThreadPool(MAX_THREADS, groupedThreads("onos/topo", "build-%d", log));
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800135 accumulator = new TopologyChangeAccumulator();
136 logConfig("Configured");
137
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800138 modified(context);
tomcbff9392014-09-10 00:45:23 -0700139
140 providerService = providerRegistry.register(this);
141 deviceService.addListener(deviceListener);
142 linkService.addListener(linkListener);
143
144 isStarted = true;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700145 triggerRecompute();
tomcbff9392014-09-10 00:45:23 -0700146 log.info("Started");
147 }
148
149 @Deactivate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800150 public synchronized void deactivate(ComponentContext context) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700151 cfgService.unregisterProperties(DefaultTopologyProvider.class, false);
tome52ce702014-09-11 00:12:54 -0700152 isStarted = false;
153
tomcbff9392014-09-10 00:45:23 -0700154 deviceService.removeListener(deviceListener);
155 linkService.removeListener(linkListener);
156 providerRegistry.unregister(this);
157 providerService = null;
158
159 executor.shutdownNow();
160 executor = null;
161
tomcbff9392014-09-10 00:45:23 -0700162 log.info("Stopped");
163 }
164
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800165 @Modified
166 public void modified(ComponentContext context) {
167 if (context == null) {
168 accumulator = new TopologyChangeAccumulator();
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800169 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800170 return;
171 }
172
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700173 Dictionary<?, ?> properties = context.getProperties();
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800174 int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
175 try {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700176 String s = get(properties, "maxEvents");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800177 newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800178
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700179 s = get(properties, "maxBatchMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800180 newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800181
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700182 s = get(properties, "maxIdleMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800183 newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s.trim());
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800184
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800185 } catch (NumberFormatException | ClassCastException e) {
Ray Milkeyd04e2272018-10-16 18:20:18 -0700186 newMaxEvents = DTP_MAX_EVENTS_DEFAULT;
187 newMaxBatchMs = DTP_MAX_BATCH_MS_DEFAULT;
188 newMaxIdleMs = DTP_MAX_IDLE_MS_DEFAULT;
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800189 }
190
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700191 if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800192 maxEvents = newMaxEvents;
193 maxBatchMs = newMaxBatchMs;
194 maxIdleMs = newMaxIdleMs;
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700195 accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800196 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800197 }
198 }
199
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800200 private void logConfig(String prefix) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700201 log.info("{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
202 prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800203 }
204
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700205
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700206 @Override
207 public void triggerRecompute() {
Sho SHIMIZU21d00692016-08-15 11:15:28 -0700208 triggerTopologyBuild(Collections.emptyList());
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700209 }
210
tomcbff9392014-09-10 00:45:23 -0700211 /**
212 * Triggers assembly of topology data citing the specified events as the
213 * reason.
214 *
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700215 * @param reasons events which triggered the topology change
tomcbff9392014-09-10 00:45:23 -0700216 */
tome52ce702014-09-11 00:12:54 -0700217 private synchronized void triggerTopologyBuild(List<Event> reasons) {
tom97937552014-09-11 10:48:42 -0700218 if (executor != null) {
219 executor.execute(new TopologyBuilderTask(reasons));
220 }
tomcbff9392014-09-10 00:45:23 -0700221 }
222
223 // Builds the topology using the latest device and link information
224 // and citing the specified events as reasons for the change.
225 private void buildTopology(List<Event> reasons) {
tomcbff9392014-09-10 00:45:23 -0700226 if (isStarted) {
tom97937552014-09-11 10:48:42 -0700227 GraphDescription desc =
228 new DefaultGraphDescription(System.nanoTime(),
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700229 System.currentTimeMillis(),
230 deviceService.getAvailableDevices(),
231 linkService.getActiveLinks());
tomcbff9392014-09-10 00:45:23 -0700232 providerService.topologyChanged(desc, reasons);
233 }
234 }
235
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800236 private void processEvent(Event event) {
237 if (accumulator != null) {
238 accumulator.add(event);
239 } else {
240 triggerTopologyBuild(ImmutableList.of(event));
241 }
242 }
243
tomcbff9392014-09-10 00:45:23 -0700244 // Callback for device events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700245 private class InternalDeviceListener implements DeviceListener {
tomcbff9392014-09-10 00:45:23 -0700246 @Override
247 public void event(DeviceEvent event) {
248 DeviceEvent.Type type = event.type();
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700249 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
250 type == DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800251 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700252 }
253 }
254 }
255
256 // Callback for link events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700257 private class InternalLinkListener implements LinkListener {
tomcbff9392014-09-10 00:45:23 -0700258 @Override
259 public void event(LinkEvent event) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800260 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700261 }
262 }
263
264 // Event accumulator for paced triggering of topology assembly.
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800265 private class TopologyChangeAccumulator extends AbstractAccumulator<Event> {
tomcbff9392014-09-10 00:45:23 -0700266 TopologyChangeAccumulator() {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800267 super(TIMER, maxEvents, maxBatchMs, maxIdleMs);
tomcbff9392014-09-10 00:45:23 -0700268 }
269
270 @Override
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800271 public void processItems(List<Event> items) {
272 triggerTopologyBuild(items);
tomcbff9392014-09-10 00:45:23 -0700273 }
tomcbff9392014-09-10 00:45:23 -0700274 }
275
276 // Task for building topology data in a separate thread.
277 private class TopologyBuilderTask implements Runnable {
278 private final List<Event> reasons;
279
280 public TopologyBuilderTask(List<Event> reasons) {
281 this.reasons = reasons;
282 }
283
284 @Override
285 public void run() {
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700286 try {
287 buildTopology(reasons);
288 } catch (Exception e) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700289 log.warn("Unable to compute topology", e);
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700290 }
tomcbff9392014-09-10 00:45:23 -0700291 }
292 }
293
294}