blob: 20692df3ea42150daaec2b3df7543bfd3098ea9d [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;
55import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
56import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
57import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
58import static org.slf4j.LoggerFactory.getLogger;
tomcbff9392014-09-10 00:45:23 -070059
60/**
Thomas Vachuska6d697f12015-03-08 20:59:50 -070061 * Default implementation of a network topology provider that feeds off
62 * device and link subsystem events to trigger assembly and computation of
63 * new topology snapshots.
tomcbff9392014-09-10 00:45:23 -070064 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065@Component(immediate = true, service = TopologyProvider.class)
Thomas Vachuska6d697f12015-03-08 20:59:50 -070066public class DefaultTopologyProvider extends AbstractProvider
67 implements TopologyProvider {
tomcbff9392014-09-10 00:45:23 -070068
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080069 private static final int MAX_THREADS = 8;
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;
Thomas Vachuska6d697f12015-03-08 20:59:50 -070075 // 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 -080076 private static final Timer TIMER = new Timer("onos-topo-event-batching");
tomcbff9392014-09-10 00:45:23 -070077
Ray Milkeyd84f89b2018-08-17 14:54:17 -070078 //@Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
79 // label = "Maximum number of events to accumulate")
Thomas Vachuska912bdd52014-11-17 11:39:01 -080080 private int maxEvents = DEFAULT_MAX_EVENTS;
81
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082 //@Property(name = "maxIdleMs", intValue = DEFAULT_MAX_IDLE_MS,
83 // label = "Maximum number of millis between events")
Thomas Vachuska912bdd52014-11-17 11:39:01 -080084 private int maxIdleMs = DEFAULT_MAX_IDLE_MS;
85
Ray Milkeyd84f89b2018-08-17 14:54:17 -070086 //@Property(name = "maxBatchMs", intValue = DEFAULT_MAX_BATCH_MS,
87 // label = "Maximum number of millis for whole batch")
Thomas Vachuska912bdd52014-11-17 11:39:01 -080088 private int maxBatchMs = DEFAULT_MAX_BATCH_MS;
89
tomcbff9392014-09-10 00:45:23 -070090 private final Logger log = getLogger(getClass());
91
Ray Milkeyd84f89b2018-08-17 14:54:17 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tomcbff9392014-09-10 00:45:23 -070093 protected TopologyProviderRegistry providerRegistry;
94
Ray Milkeyd84f89b2018-08-17 14:54:17 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tomcbff9392014-09-10 00:45:23 -070096 protected DeviceService deviceService;
97
Ray Milkeyd84f89b2018-08-17 14:54:17 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY)
tomcbff9392014-09-10 00:45:23 -070099 protected LinkService linkService;
100
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700102 protected ComponentConfigService cfgService;
103
tomcbff9392014-09-10 00:45:23 -0700104 private volatile boolean isStarted = false;
105
106 private TopologyProviderService providerService;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500107 private final DeviceListener deviceListener = new InternalDeviceListener();
108 private final LinkListener linkListener = new InternalLinkListener();
tomcbff9392014-09-10 00:45:23 -0700109
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800110 private Accumulator<Event> accumulator;
tomcbff9392014-09-10 00:45:23 -0700111 private ExecutorService executor;
112
113 /**
114 * Creates a provider with the supplier identifier.
115 */
tom97937552014-09-11 10:48:42 -0700116 public DefaultTopologyProvider() {
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800117 super(CORE_PROVIDER_ID);
tomcbff9392014-09-10 00:45:23 -0700118 }
119
120 @Activate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800121 public synchronized void activate(ComponentContext context) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700122 cfgService.registerProperties(DefaultTopologyProvider.class);
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700123 executor = newFixedThreadPool(MAX_THREADS, groupedThreads("onos/topo", "build-%d", log));
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800124 accumulator = new TopologyChangeAccumulator();
125 logConfig("Configured");
126
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800127 modified(context);
tomcbff9392014-09-10 00:45:23 -0700128
129 providerService = providerRegistry.register(this);
130 deviceService.addListener(deviceListener);
131 linkService.addListener(linkListener);
132
133 isStarted = true;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700134 triggerRecompute();
tomcbff9392014-09-10 00:45:23 -0700135 log.info("Started");
136 }
137
138 @Deactivate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800139 public synchronized void deactivate(ComponentContext context) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700140 cfgService.unregisterProperties(DefaultTopologyProvider.class, false);
tome52ce702014-09-11 00:12:54 -0700141 isStarted = false;
142
tomcbff9392014-09-10 00:45:23 -0700143 deviceService.removeListener(deviceListener);
144 linkService.removeListener(linkListener);
145 providerRegistry.unregister(this);
146 providerService = null;
147
148 executor.shutdownNow();
149 executor = null;
150
tomcbff9392014-09-10 00:45:23 -0700151 log.info("Stopped");
152 }
153
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800154 @Modified
155 public void modified(ComponentContext context) {
156 if (context == null) {
157 accumulator = new TopologyChangeAccumulator();
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800158 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800159 return;
160 }
161
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700162 Dictionary<?, ?> properties = context.getProperties();
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800163 int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
164 try {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700165 String s = get(properties, "maxEvents");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800166 newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800167
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700168 s = get(properties, "maxBatchMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800169 newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800170
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700171 s = get(properties, "maxIdleMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800172 newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s.trim());
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800173
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800174 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800175 newMaxEvents = DEFAULT_MAX_EVENTS;
176 newMaxBatchMs = DEFAULT_MAX_BATCH_MS;
177 newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
178 }
179
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700180 if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800181 maxEvents = newMaxEvents;
182 maxBatchMs = newMaxBatchMs;
183 maxIdleMs = newMaxIdleMs;
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700184 accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800185 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800186 }
187 }
188
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800189 private void logConfig(String prefix) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700190 log.info("{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
191 prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800192 }
193
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700194
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700195 @Override
196 public void triggerRecompute() {
Sho SHIMIZU21d00692016-08-15 11:15:28 -0700197 triggerTopologyBuild(Collections.emptyList());
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700198 }
199
tomcbff9392014-09-10 00:45:23 -0700200 /**
201 * Triggers assembly of topology data citing the specified events as the
202 * reason.
203 *
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700204 * @param reasons events which triggered the topology change
tomcbff9392014-09-10 00:45:23 -0700205 */
tome52ce702014-09-11 00:12:54 -0700206 private synchronized void triggerTopologyBuild(List<Event> reasons) {
tom97937552014-09-11 10:48:42 -0700207 if (executor != null) {
208 executor.execute(new TopologyBuilderTask(reasons));
209 }
tomcbff9392014-09-10 00:45:23 -0700210 }
211
212 // Builds the topology using the latest device and link information
213 // and citing the specified events as reasons for the change.
214 private void buildTopology(List<Event> reasons) {
tomcbff9392014-09-10 00:45:23 -0700215 if (isStarted) {
tom97937552014-09-11 10:48:42 -0700216 GraphDescription desc =
217 new DefaultGraphDescription(System.nanoTime(),
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700218 System.currentTimeMillis(),
219 deviceService.getAvailableDevices(),
220 linkService.getActiveLinks());
tomcbff9392014-09-10 00:45:23 -0700221 providerService.topologyChanged(desc, reasons);
222 }
223 }
224
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800225 private void processEvent(Event event) {
226 if (accumulator != null) {
227 accumulator.add(event);
228 } else {
229 triggerTopologyBuild(ImmutableList.of(event));
230 }
231 }
232
tomcbff9392014-09-10 00:45:23 -0700233 // Callback for device events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700234 private class InternalDeviceListener implements DeviceListener {
tomcbff9392014-09-10 00:45:23 -0700235 @Override
236 public void event(DeviceEvent event) {
237 DeviceEvent.Type type = event.type();
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700238 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
239 type == DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800240 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700241 }
242 }
243 }
244
245 // Callback for link events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700246 private class InternalLinkListener implements LinkListener {
tomcbff9392014-09-10 00:45:23 -0700247 @Override
248 public void event(LinkEvent event) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800249 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700250 }
251 }
252
253 // Event accumulator for paced triggering of topology assembly.
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800254 private class TopologyChangeAccumulator extends AbstractAccumulator<Event> {
tomcbff9392014-09-10 00:45:23 -0700255 TopologyChangeAccumulator() {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800256 super(TIMER, maxEvents, maxBatchMs, maxIdleMs);
tomcbff9392014-09-10 00:45:23 -0700257 }
258
259 @Override
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800260 public void processItems(List<Event> items) {
261 triggerTopologyBuild(items);
tomcbff9392014-09-10 00:45:23 -0700262 }
tomcbff9392014-09-10 00:45:23 -0700263 }
264
265 // Task for building topology data in a separate thread.
266 private class TopologyBuilderTask implements Runnable {
267 private final List<Event> reasons;
268
269 public TopologyBuilderTask(List<Event> reasons) {
270 this.reasons = reasons;
271 }
272
273 @Override
274 public void run() {
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700275 try {
276 buildTopology(reasons);
277 } catch (Exception e) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700278 log.warn("Unable to compute topology", e);
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700279 }
tomcbff9392014-09-10 00:45:23 -0700280 }
281 }
282
283}