blob: 3f6e28528dd7cea58e41efa6cc16ab24660e0b74 [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 */
tombe988312014-09-19 18:38:47 -070016package org.onlab.onos.net.topology.impl;
tomcbff9392014-09-10 00:45:23 -070017
Thomas Vachuska912bdd52014-11-17 11:39:01 -080018import com.google.common.collect.ImmutableList;
tomcbff9392014-09-10 00:45:23 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080022import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
tomcbff9392014-09-10 00:45:23 -070024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070026import org.apache.felix.scr.annotations.Service;
tomcbff9392014-09-10 00:45:23 -070027import org.onlab.onos.event.AbstractEventAccumulator;
28import org.onlab.onos.event.Event;
29import org.onlab.onos.event.EventAccumulator;
30import org.onlab.onos.net.device.DeviceEvent;
31import org.onlab.onos.net.device.DeviceListener;
32import org.onlab.onos.net.device.DeviceService;
33import org.onlab.onos.net.link.LinkEvent;
34import org.onlab.onos.net.link.LinkListener;
35import org.onlab.onos.net.link.LinkService;
36import org.onlab.onos.net.provider.AbstractProvider;
tombe988312014-09-19 18:38:47 -070037import org.onlab.onos.net.topology.DefaultGraphDescription;
tom97937552014-09-11 10:48:42 -070038import org.onlab.onos.net.topology.GraphDescription;
tomcbff9392014-09-10 00:45:23 -070039import org.onlab.onos.net.topology.TopologyProvider;
40import org.onlab.onos.net.topology.TopologyProviderRegistry;
41import org.onlab.onos.net.topology.TopologyProviderService;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080042import org.osgi.service.component.ComponentContext;
tomcbff9392014-09-10 00:45:23 -070043import org.slf4j.Logger;
44
Pavlin Radoslavova0e47542014-10-17 19:22:17 -070045import java.util.Collections;
Thomas Vachuska912bdd52014-11-17 11:39:01 -080046import java.util.Dictionary;
tomcbff9392014-09-10 00:45:23 -070047import java.util.List;
48import java.util.Timer;
49import java.util.concurrent.ExecutorService;
50
Thomas Vachuska912bdd52014-11-17 11:39:01 -080051import static com.google.common.base.Strings.isNullOrEmpty;
tomcbff9392014-09-10 00:45:23 -070052import static java.util.concurrent.Executors.newFixedThreadPool;
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -080053import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID;
tomcbff9392014-09-10 00:45:23 -070054import static org.onlab.onos.net.device.DeviceEvent.Type.*;
55import static org.onlab.util.Tools.namedThreads;
56import static org.slf4j.LoggerFactory.getLogger;
57
58/**
tom578ebdc2014-09-11 11:12:51 -070059 * Default implementation of a network topology provider that feeds off
60 * device and link subsystem events to trigger assembly and computation of
61 * new topology snapshots.
tomcbff9392014-09-10 00:45:23 -070062 */
63@Component(immediate = true)
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070064@Service
tom97937552014-09-11 10:48:42 -070065public class DefaultTopologyProvider extends AbstractProvider
tomcbff9392014-09-10 00:45:23 -070066 implements TopologyProvider {
67
tomcbff9392014-09-10 00:45:23 -070068 private static final int MAX_THREADS = 8;
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080069 private static final int DEFAULT_MAX_EVENTS = 200;
70 private static final int DEFAULT_MAX_BATCH_MS = 60;
71 private static final int DEFAULT_MAX_IDLE_MS = 30;
tomcbff9392014-09-10 00:45:23 -070072
tom025e09f2014-09-15 15:29:24 -070073 // FIXME: Replace with a system-wide timer instance;
74 // TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
Thomas Vachuska5bde31f2014-11-25 15:29:18 -080075 private static final Timer TIMER = new Timer("topo-event-batching");
tomcbff9392014-09-10 00:45:23 -070076
Thomas Vachuska912bdd52014-11-17 11:39:01 -080077 @Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
78 label = "Maximum number of events to accumulate")
79 private int maxEvents = DEFAULT_MAX_EVENTS;
80
81 @Property(name = "maxIdleMs", intValue = DEFAULT_MAX_IDLE_MS,
82 label = "Maximum number of millis between events")
83 private int maxIdleMs = DEFAULT_MAX_IDLE_MS;
84
85 @Property(name = "maxBatchMs", intValue = DEFAULT_MAX_BATCH_MS,
86 label = "Maximum number of millis for whole batch")
87 private int maxBatchMs = DEFAULT_MAX_BATCH_MS;
88
tomcbff9392014-09-10 00:45:23 -070089 private final Logger log = getLogger(getClass());
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected TopologyProviderRegistry providerRegistry;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DeviceService deviceService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected LinkService linkService;
99
100 private volatile boolean isStarted = false;
101
102 private TopologyProviderService providerService;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700103 private DeviceListener deviceListener = new InternalDeviceListener();
104 private LinkListener linkListener = new InternalLinkListener();
tomcbff9392014-09-10 00:45:23 -0700105
106 private EventAccumulator accumulator;
107 private ExecutorService executor;
108
109 /**
110 * Creates a provider with the supplier identifier.
111 */
tom97937552014-09-11 10:48:42 -0700112 public DefaultTopologyProvider() {
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800113 super(CORE_PROVIDER_ID);
tomcbff9392014-09-10 00:45:23 -0700114 }
115
116 @Activate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800117 public synchronized void activate(ComponentContext context) {
tom578ebdc2014-09-11 11:12:51 -0700118 executor = newFixedThreadPool(MAX_THREADS, namedThreads("topo-build-%d"));
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800119 modified(context);
tomcbff9392014-09-10 00:45:23 -0700120
121 providerService = providerRegistry.register(this);
122 deviceService.addListener(deviceListener);
123 linkService.addListener(linkListener);
124
Thomas Vachuska5bde31f2014-11-25 15:29:18 -0800125 log.info("Configured with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}",
126 maxEvents, maxBatchMs, maxIdleMs);
127
tomcbff9392014-09-10 00:45:23 -0700128 isStarted = true;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700129 triggerRecompute();
tomcbff9392014-09-10 00:45:23 -0700130 log.info("Started");
131 }
132
133 @Deactivate
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800134 public synchronized void deactivate(ComponentContext context) {
tome52ce702014-09-11 00:12:54 -0700135 isStarted = false;
136
tomcbff9392014-09-10 00:45:23 -0700137 deviceService.removeListener(deviceListener);
138 linkService.removeListener(linkListener);
139 providerRegistry.unregister(this);
140 providerService = null;
141
142 executor.shutdownNow();
143 executor = null;
144
tomcbff9392014-09-10 00:45:23 -0700145 log.info("Stopped");
146 }
147
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800148 @Modified
149 public void modified(ComponentContext context) {
150 if (context == null) {
151 accumulator = new TopologyChangeAccumulator();
152 return;
153 }
154
155 Dictionary properties = context.getProperties();
156 int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
157 try {
158 String s = (String) properties.get("maxEvents");
159 newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s);
160
161 s = (String) properties.get("maxBatchMs");
162 newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s);
163
164 s = (String) properties.get("maxIdleMs");
165 newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s);
166 } catch (Exception e) {
167 newMaxEvents = DEFAULT_MAX_EVENTS;
168 newMaxBatchMs = DEFAULT_MAX_BATCH_MS;
169 newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
170 }
171
172 if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
173 maxEvents = newMaxEvents;
174 maxBatchMs = newMaxBatchMs;
175 maxIdleMs = newMaxIdleMs;
176 accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
177 log.info("Reconfigured with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}",
178 maxEvents, maxBatchMs, maxIdleMs);
179 }
180 }
181
182
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700183 @Override
184 public void triggerRecompute() {
185 triggerTopologyBuild(Collections.<Event>emptyList());
186 }
187
tomcbff9392014-09-10 00:45:23 -0700188 /**
189 * Triggers assembly of topology data citing the specified events as the
190 * reason.
191 *
192 * @param reasons events which triggered the topology change
193 */
tome52ce702014-09-11 00:12:54 -0700194 private synchronized void triggerTopologyBuild(List<Event> reasons) {
tom97937552014-09-11 10:48:42 -0700195 if (executor != null) {
196 executor.execute(new TopologyBuilderTask(reasons));
197 }
tomcbff9392014-09-10 00:45:23 -0700198 }
199
200 // Builds the topology using the latest device and link information
201 // and citing the specified events as reasons for the change.
202 private void buildTopology(List<Event> reasons) {
tomcbff9392014-09-10 00:45:23 -0700203 if (isStarted) {
tom97937552014-09-11 10:48:42 -0700204 GraphDescription desc =
205 new DefaultGraphDescription(System.nanoTime(),
206 deviceService.getDevices(),
207 linkService.getLinks());
tomcbff9392014-09-10 00:45:23 -0700208 providerService.topologyChanged(desc, reasons);
209 }
210 }
211
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800212 private void processEvent(Event event) {
213 if (accumulator != null) {
214 accumulator.add(event);
215 } else {
216 triggerTopologyBuild(ImmutableList.of(event));
217 }
218 }
219
tomcbff9392014-09-10 00:45:23 -0700220 // Callback for device events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700221 private class InternalDeviceListener implements DeviceListener {
tomcbff9392014-09-10 00:45:23 -0700222 @Override
223 public void event(DeviceEvent event) {
224 DeviceEvent.Type type = event.type();
225 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
226 type == DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800227 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700228 }
229 }
230 }
231
232 // Callback for link events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700233 private class InternalLinkListener implements LinkListener {
tomcbff9392014-09-10 00:45:23 -0700234 @Override
235 public void event(LinkEvent event) {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800236 processEvent(event);
tomcbff9392014-09-10 00:45:23 -0700237 }
238 }
239
240 // Event accumulator for paced triggering of topology assembly.
241 private class TopologyChangeAccumulator
242 extends AbstractEventAccumulator implements EventAccumulator {
243
244 TopologyChangeAccumulator() {
Thomas Vachuska912bdd52014-11-17 11:39:01 -0800245 super(TIMER, maxEvents, maxBatchMs, maxIdleMs);
tomcbff9392014-09-10 00:45:23 -0700246 }
247
248 @Override
249 public void processEvents(List<Event> events) {
250 triggerTopologyBuild(events);
251 }
252
253 }
254
255 // Task for building topology data in a separate thread.
256 private class TopologyBuilderTask implements Runnable {
257 private final List<Event> reasons;
258
259 public TopologyBuilderTask(List<Event> reasons) {
260 this.reasons = reasons;
261 }
262
263 @Override
264 public void run() {
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700265 try {
266 buildTopology(reasons);
267 } catch (Exception e) {
268 log.warn("Unable to compute topology due to: {}", e.getMessage());
Yuta HIGUCHI22102822014-11-12 23:09:59 -0800269 log.debug("Unable to compute topology", e);
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700270 }
tomcbff9392014-09-10 00:45:23 -0700271 }
272 }
273
274}