blob: 1fac99fc750990390d3eb613d5907608e387d924 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present Open Networking Laboratory
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
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050018import static com.google.common.base.Strings.isNullOrEmpty;
19import static java.util.concurrent.Executors.newFixedThreadPool;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070020import static org.onlab.util.Tools.get;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050021import static org.onlab.util.Tools.groupedThreads;
22import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
23import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
24import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
25import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
26import static org.slf4j.LoggerFactory.getLogger;
27
28import java.util.Collections;
29import java.util.Dictionary;
30import java.util.List;
31import java.util.Timer;
32import java.util.concurrent.ExecutorService;
33
tomcbff9392014-09-10 00:45:23 -070034import org.apache.felix.scr.annotations.Activate;
35import org.apache.felix.scr.annotations.Component;
36import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080037import org.apache.felix.scr.annotations.Modified;
38import org.apache.felix.scr.annotations.Property;
tomcbff9392014-09-10 00:45:23 -070039import org.apache.felix.scr.annotations.Reference;
40import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070041import org.apache.felix.scr.annotations.Service;
Thomas Vachuskaecb63c52015-02-19 10:03:48 -080042import org.onlab.util.AbstractAccumulator;
43import org.onlab.util.Accumulator;
Thomas Vachuska6d697f12015-03-08 20:59:50 -070044import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.event.Event;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.device.DeviceEvent;
47import org.onosproject.net.device.DeviceListener;
48import org.onosproject.net.device.DeviceService;
49import org.onosproject.net.link.LinkEvent;
50import org.onosproject.net.link.LinkListener;
51import org.onosproject.net.link.LinkService;
52import org.onosproject.net.provider.AbstractProvider;
53import org.onosproject.net.topology.DefaultGraphDescription;
54import org.onosproject.net.topology.GraphDescription;
55import org.onosproject.net.topology.TopologyProvider;
56import org.onosproject.net.topology.TopologyProviderRegistry;
57import org.onosproject.net.topology.TopologyProviderService;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080058import org.osgi.service.component.ComponentContext;
tomcbff9392014-09-10 00:45:23 -070059import org.slf4j.Logger;
60
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050061import com.google.common.collect.ImmutableList;
tomcbff9392014-09-10 00:45:23 -070062
63/**
Thomas Vachuska6d697f12015-03-08 20:59:50 -070064 * Default implementation of a network topology provider that feeds off
65 * device and link subsystem events to trigger assembly and computation of
66 * new topology snapshots.
tomcbff9392014-09-10 00:45:23 -070067 */
68@Component(immediate = true)
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070069@Service
Thomas Vachuska6d697f12015-03-08 20:59:50 -070070public class DefaultTopologyProvider extends AbstractProvider
71 implements TopologyProvider {
tomcbff9392014-09-10 00:45:23 -070072
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080073 private static final int MAX_THREADS = 8;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -080074 private static final int DEFAULT_MAX_EVENTS = 1000;
75 private static final int DEFAULT_MAX_IDLE_MS = 10;
76 private static final int DEFAULT_MAX_BATCH_MS = 50;
tomcbff9392014-09-10 00:45:23 -070077
tom025e09f2014-09-15 15:29:24 -070078 // FIXME: Replace with a system-wide timer instance;
Thomas Vachuska6d697f12015-03-08 20:59:50 -070079 // 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 -080080 private static final Timer TIMER = new Timer("onos-topo-event-batching");
tomcbff9392014-09-10 00:45:23 -070081
Thomas Vachuska912bdd52014-11-17 11:39:01 -080082 @Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
83 label = "Maximum number of events to accumulate")
84 private int maxEvents = DEFAULT_MAX_EVENTS;
85
86 @Property(name = "maxIdleMs", intValue = DEFAULT_MAX_IDLE_MS,
87 label = "Maximum number of millis between events")
88 private int maxIdleMs = DEFAULT_MAX_IDLE_MS;
89
90 @Property(name = "maxBatchMs", intValue = DEFAULT_MAX_BATCH_MS,
91 label = "Maximum number of millis for whole batch")
92 private int maxBatchMs = DEFAULT_MAX_BATCH_MS;
93
tomcbff9392014-09-10 00:45:23 -070094 private final Logger log = getLogger(getClass());
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected TopologyProviderRegistry providerRegistry;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DeviceService deviceService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected LinkService linkService;
104
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected ComponentConfigService cfgService;
107
tomcbff9392014-09-10 00:45:23 -0700108 private volatile boolean isStarted = false;
109
110 private TopologyProviderService providerService;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500111 private final DeviceListener deviceListener = new InternalDeviceListener();
112 private final LinkListener linkListener = new InternalLinkListener();
tomcbff9392014-09-10 00:45:23 -0700113
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800114 private Accumulator<Event> accumulator;
tomcbff9392014-09-10 00:45:23 -0700115 private ExecutorService executor;
116
117 /**
118 * Creates a provider with the supplier identifier.
119 */
tom97937552014-09-11 10:48:42 -0700120 public DefaultTopologyProvider() {
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800121 super(CORE_PROVIDER_ID);
tomcbff9392014-09-10 00:45:23 -0700122 }
123
124 @Activate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800125 public synchronized void activate(ComponentContext context) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700126 cfgService.registerProperties(DefaultTopologyProvider.class);
127 executor = newFixedThreadPool(MAX_THREADS, groupedThreads("onos/topo", "build-%d"));
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800128 accumulator = new TopologyChangeAccumulator();
129 logConfig("Configured");
130
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800131 modified(context);
tomcbff9392014-09-10 00:45:23 -0700132
133 providerService = providerRegistry.register(this);
134 deviceService.addListener(deviceListener);
135 linkService.addListener(linkListener);
136
137 isStarted = true;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700138 triggerRecompute();
tomcbff9392014-09-10 00:45:23 -0700139 log.info("Started");
140 }
141
142 @Deactivate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800143 public synchronized void deactivate(ComponentContext context) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700144 cfgService.unregisterProperties(DefaultTopologyProvider.class, false);
tome52ce702014-09-11 00:12:54 -0700145 isStarted = false;
146
tomcbff9392014-09-10 00:45:23 -0700147 deviceService.removeListener(deviceListener);
148 linkService.removeListener(linkListener);
149 providerRegistry.unregister(this);
150 providerService = null;
151
152 executor.shutdownNow();
153 executor = null;
154
tomcbff9392014-09-10 00:45:23 -0700155 log.info("Stopped");
156 }
157
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800158 @Modified
159 public void modified(ComponentContext context) {
160 if (context == null) {
161 accumulator = new TopologyChangeAccumulator();
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800162 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800163 return;
164 }
165
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700166 Dictionary<?, ?> properties = context.getProperties();
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800167 int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
168 try {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700169 String s = get(properties, "maxEvents");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800170 newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800171
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700172 s = get(properties, "maxBatchMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800173 newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800174
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700175 s = get(properties, "maxIdleMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800176 newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s.trim());
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800177
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800178 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800179 newMaxEvents = DEFAULT_MAX_EVENTS;
180 newMaxBatchMs = DEFAULT_MAX_BATCH_MS;
181 newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
182 }
183
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700184 if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800185 maxEvents = newMaxEvents;
186 maxBatchMs = newMaxBatchMs;
187 maxIdleMs = newMaxIdleMs;
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700188 accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800189 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800190 }
191 }
192
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800193 private void logConfig(String prefix) {
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700194 log.info("{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
195 prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800196 }
197
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700198
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700199 @Override
200 public void triggerRecompute() {
201 triggerTopologyBuild(Collections.<Event>emptyList());
202 }
203
tomcbff9392014-09-10 00:45:23 -0700204 /**
205 * Triggers assembly of topology data citing the specified events as the
206 * reason.
207 *
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700208 * @param reasons events which triggered the topology change
tomcbff9392014-09-10 00:45:23 -0700209 */
tome52ce702014-09-11 00:12:54 -0700210 private synchronized void triggerTopologyBuild(List<Event> reasons) {
tom97937552014-09-11 10:48:42 -0700211 if (executor != null) {
212 executor.execute(new TopologyBuilderTask(reasons));
213 }
tomcbff9392014-09-10 00:45:23 -0700214 }
215
216 // Builds the topology using the latest device and link information
217 // and citing the specified events as reasons for the change.
218 private void buildTopology(List<Event> reasons) {
tomcbff9392014-09-10 00:45:23 -0700219 if (isStarted) {
tom97937552014-09-11 10:48:42 -0700220 GraphDescription desc =
221 new DefaultGraphDescription(System.nanoTime(),
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700222 System.currentTimeMillis(),
223 deviceService.getAvailableDevices(),
224 linkService.getActiveLinks());
tomcbff9392014-09-10 00:45:23 -0700225 providerService.topologyChanged(desc, reasons);
226 }
227 }
228
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800229 private void processEvent(Event event) {
230 if (accumulator != null) {
231 accumulator.add(event);
232 } else {
233 triggerTopologyBuild(ImmutableList.of(event));
234 }
235 }
236
tomcbff9392014-09-10 00:45:23 -0700237 // Callback for device events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700238 private class InternalDeviceListener implements DeviceListener {
tomcbff9392014-09-10 00:45:23 -0700239 @Override
240 public void event(DeviceEvent event) {
241 DeviceEvent.Type type = event.type();
Thomas Vachuska6d697f12015-03-08 20:59:50 -0700242 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
243 type == DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800244 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700245 }
246 }
247 }
248
249 // Callback for link events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700250 private class InternalLinkListener implements LinkListener {
tomcbff9392014-09-10 00:45:23 -0700251 @Override
252 public void event(LinkEvent event) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800253 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700254 }
255 }
256
257 // Event accumulator for paced triggering of topology assembly.
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800258 private class TopologyChangeAccumulator extends AbstractAccumulator<Event> {
tomcbff9392014-09-10 00:45:23 -0700259 TopologyChangeAccumulator() {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800260 super(TIMER, maxEvents, maxBatchMs, maxIdleMs);
tomcbff9392014-09-10 00:45:23 -0700261 }
262
263 @Override
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800264 public void processItems(List<Event> items) {
265 triggerTopologyBuild(items);
tomcbff9392014-09-10 00:45:23 -0700266 }
tomcbff9392014-09-10 00:45:23 -0700267 }
268
269 // Task for building topology data in a separate thread.
270 private class TopologyBuilderTask implements Runnable {
271 private final List<Event> reasons;
272
273 public TopologyBuilderTask(List<Event> reasons) {
274 this.reasons = reasons;
275 }
276
277 @Override
278 public void run() {
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700279 try {
280 buildTopology(reasons);
281 } catch (Exception e) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700282 log.warn("Unable to compute topology", e);
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700283 }
tomcbff9392014-09-10 00:45:23 -0700284 }
285 }
286
287}