blob: af405952cf5c4635c7af535eb1210b7bde1a0dac [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;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.event.Event;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.device.DeviceEvent;
45import org.onosproject.net.device.DeviceListener;
46import org.onosproject.net.device.DeviceService;
47import org.onosproject.net.link.LinkEvent;
48import org.onosproject.net.link.LinkListener;
49import org.onosproject.net.link.LinkService;
50import org.onosproject.net.provider.AbstractProvider;
51import org.onosproject.net.topology.DefaultGraphDescription;
52import org.onosproject.net.topology.GraphDescription;
53import org.onosproject.net.topology.TopologyProvider;
54import org.onosproject.net.topology.TopologyProviderRegistry;
55import org.onosproject.net.topology.TopologyProviderService;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080056import org.osgi.service.component.ComponentContext;
tomcbff9392014-09-10 00:45:23 -070057import org.slf4j.Logger;
58
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050059import com.google.common.collect.ImmutableList;
tomcbff9392014-09-10 00:45:23 -070060
61/**
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050062 * Default implementation of a network topology provider that feeds off device
63 * and link subsystem events to trigger assembly and computation of new topology
64 * snapshots.
tomcbff9392014-09-10 00:45:23 -070065 */
66@Component(immediate = true)
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070067@Service
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050068public class DefaultTopologyProvider extends AbstractProvider implements TopologyProvider {
tomcbff9392014-09-10 00:45:23 -070069
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080070 private static final int MAX_THREADS = 8;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -080071 private static final int DEFAULT_MAX_EVENTS = 1000;
72 private static final int DEFAULT_MAX_IDLE_MS = 10;
73 private static final int DEFAULT_MAX_BATCH_MS = 50;
tomcbff9392014-09-10 00:45:23 -070074
tom025e09f2014-09-15 15:29:24 -070075 // FIXME: Replace with a system-wide timer instance;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050076 // TODO: Convert to use HashedWheelTimer or produce a variant of that; then
77 // decide which we want to adopt
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080078 private static final Timer TIMER = new Timer("onos-topo-event-batching");
tomcbff9392014-09-10 00:45:23 -070079
Thomas Vachuska912bdd52014-11-17 11:39:01 -080080 @Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
81 label = "Maximum number of events to accumulate")
82 private int maxEvents = DEFAULT_MAX_EVENTS;
83
84 @Property(name = "maxIdleMs", intValue = DEFAULT_MAX_IDLE_MS,
85 label = "Maximum number of millis between events")
86 private int maxIdleMs = DEFAULT_MAX_IDLE_MS;
87
88 @Property(name = "maxBatchMs", intValue = DEFAULT_MAX_BATCH_MS,
89 label = "Maximum number of millis for whole batch")
90 private int maxBatchMs = DEFAULT_MAX_BATCH_MS;
91
tomcbff9392014-09-10 00:45:23 -070092 private final Logger log = getLogger(getClass());
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected TopologyProviderRegistry providerRegistry;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected DeviceService deviceService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected LinkService linkService;
102
103 private volatile boolean isStarted = false;
104
105 private TopologyProviderService providerService;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500106 private final DeviceListener deviceListener = new InternalDeviceListener();
107 private final LinkListener linkListener = new InternalLinkListener();
tomcbff9392014-09-10 00:45:23 -0700108
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800109 private Accumulator<Event> accumulator;
tomcbff9392014-09-10 00:45:23 -0700110 private ExecutorService executor;
111
112 /**
113 * Creates a provider with the supplier identifier.
114 */
tom97937552014-09-11 10:48:42 -0700115 public DefaultTopologyProvider() {
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800116 super(CORE_PROVIDER_ID);
tomcbff9392014-09-10 00:45:23 -0700117 }
118
119 @Activate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800120 public synchronized void activate(ComponentContext context) {
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500121 executor = newFixedThreadPool(MAX_THREADS,
122 groupedThreads("onos/topo", "build-%d"));
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800123 accumulator = new TopologyChangeAccumulator();
124 logConfig("Configured");
125
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800126 modified(context);
tomcbff9392014-09-10 00:45:23 -0700127
128 providerService = providerRegistry.register(this);
129 deviceService.addListener(deviceListener);
130 linkService.addListener(linkListener);
131
132 isStarted = true;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700133 triggerRecompute();
tomcbff9392014-09-10 00:45:23 -0700134 log.info("Started");
135 }
136
137 @Deactivate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800138 public synchronized void deactivate(ComponentContext context) {
tome52ce702014-09-11 00:12:54 -0700139 isStarted = false;
140
tomcbff9392014-09-10 00:45:23 -0700141 deviceService.removeListener(deviceListener);
142 linkService.removeListener(linkListener);
143 providerRegistry.unregister(this);
144 providerService = null;
145
146 executor.shutdownNow();
147 executor = null;
148
tomcbff9392014-09-10 00:45:23 -0700149 log.info("Stopped");
150 }
151
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800152 @Modified
153 public void modified(ComponentContext context) {
154 if (context == null) {
155 accumulator = new TopologyChangeAccumulator();
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800156 logConfig("Reconfigured");
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800157 return;
158 }
159
160 Dictionary properties = context.getProperties();
161 int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
162 try {
163 String s = (String) properties.get("maxEvents");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800164 newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800165
166 s = (String) properties.get("maxBatchMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800167 newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s.trim());
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800168
169 s = (String) properties.get("maxIdleMs");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800170 newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s.trim());
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800171
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800172 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800173 newMaxEvents = DEFAULT_MAX_EVENTS;
174 newMaxBatchMs = DEFAULT_MAX_BATCH_MS;
175 newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
176 }
177
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500178 if ((newMaxEvents != maxEvents) || (newMaxBatchMs != maxBatchMs)
179 || (newMaxIdleMs != maxIdleMs)) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800180 maxEvents = newMaxEvents;
181 maxBatchMs = newMaxBatchMs;
182 maxIdleMs = newMaxIdleMs;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500183 accumulator = maxEvents > 1 ? new TopologyChangeAccumulator()
184 : 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) {
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500190 log.info(
191 "{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
192 prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800193 }
194
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700195 @Override
196 public void triggerRecompute() {
197 triggerTopologyBuild(Collections.<Event>emptyList());
198 }
199
tomcbff9392014-09-10 00:45:23 -0700200 /**
201 * Triggers assembly of topology data citing the specified events as the
202 * reason.
203 *
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500204 * @param reasons
205 * events which triggered the topology change
tomcbff9392014-09-10 00:45:23 -0700206 */
tome52ce702014-09-11 00:12:54 -0700207 private synchronized void triggerTopologyBuild(List<Event> reasons) {
tom97937552014-09-11 10:48:42 -0700208 if (executor != null) {
209 executor.execute(new TopologyBuilderTask(reasons));
210 }
tomcbff9392014-09-10 00:45:23 -0700211 }
212
213 // Builds the topology using the latest device and link information
214 // and citing the specified events as reasons for the change.
215 private void buildTopology(List<Event> reasons) {
tomcbff9392014-09-10 00:45:23 -0700216 if (isStarted) {
tom97937552014-09-11 10:48:42 -0700217 GraphDescription desc =
218 new DefaultGraphDescription(System.nanoTime(),
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500219 System.currentTimeMillis(),
220 deviceService.getAvailableDevices(),
221 linkService.getActiveLinks());
tomcbff9392014-09-10 00:45:23 -0700222 providerService.topologyChanged(desc, reasons);
223 }
224 }
225
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800226 private void processEvent(Event event) {
227 if (accumulator != null) {
228 accumulator.add(event);
229 } else {
230 triggerTopologyBuild(ImmutableList.of(event));
231 }
232 }
233
tomcbff9392014-09-10 00:45:23 -0700234 // Callback for device events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700235 private class InternalDeviceListener implements DeviceListener {
tomcbff9392014-09-10 00:45:23 -0700236 @Override
237 public void event(DeviceEvent event) {
238 DeviceEvent.Type type = event.type();
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500239 if ((type == DEVICE_ADDED) || (type == DEVICE_REMOVED) ||
240 (type == DEVICE_AVAILABILITY_CHANGED)) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800241 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700242 }
243 }
244 }
245
246 // Callback for link events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700247 private class InternalLinkListener implements LinkListener {
tomcbff9392014-09-10 00:45:23 -0700248 @Override
249 public void event(LinkEvent event) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800250 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700251 }
252 }
253
254 // Event accumulator for paced triggering of topology assembly.
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800255 private class TopologyChangeAccumulator extends AbstractAccumulator<Event> {
tomcbff9392014-09-10 00:45:23 -0700256 TopologyChangeAccumulator() {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800257 super(TIMER, maxEvents, maxBatchMs, maxIdleMs);
tomcbff9392014-09-10 00:45:23 -0700258 }
259
260 @Override
Thomas Vachuskaecb63c52015-02-19 10:03:48 -0800261 public void processItems(List<Event> items) {
262 triggerTopologyBuild(items);
tomcbff9392014-09-10 00:45:23 -0700263 }
tomcbff9392014-09-10 00:45:23 -0700264 }
265
266 // Task for building topology data in a separate thread.
267 private class TopologyBuilderTask implements Runnable {
268 private final List<Event> reasons;
269
270 public TopologyBuilderTask(List<Event> reasons) {
271 this.reasons = reasons;
272 }
273
274 @Override
275 public void run() {
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700276 try {
277 buildTopology(reasons);
278 } catch (Exception e) {
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500279 log.warn("Unable to compute topology due to: {}",
280 e.getMessage());
Yuta HIGUCHI22102822014-11-12 23:09:59 -0800281 log.debug("Unable to compute topology", e);
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700282 }
tomcbff9392014-09-10 00:45:23 -0700283 }
284 }
285
286}