blob: a3e0d6c3fcdbc490a937ca797a9c385b0fe2535c [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
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070023import org.apache.felix.scr.annotations.Service;
tomcbff9392014-09-10 00:45:23 -070024import org.onlab.onos.event.AbstractEventAccumulator;
25import org.onlab.onos.event.Event;
26import org.onlab.onos.event.EventAccumulator;
27import org.onlab.onos.net.device.DeviceEvent;
28import org.onlab.onos.net.device.DeviceListener;
29import org.onlab.onos.net.device.DeviceService;
30import org.onlab.onos.net.link.LinkEvent;
31import org.onlab.onos.net.link.LinkListener;
32import org.onlab.onos.net.link.LinkService;
33import org.onlab.onos.net.provider.AbstractProvider;
tombe988312014-09-19 18:38:47 -070034import org.onlab.onos.net.topology.DefaultGraphDescription;
tom97937552014-09-11 10:48:42 -070035import org.onlab.onos.net.topology.GraphDescription;
tomcbff9392014-09-10 00:45:23 -070036import org.onlab.onos.net.topology.TopologyProvider;
37import org.onlab.onos.net.topology.TopologyProviderRegistry;
38import org.onlab.onos.net.topology.TopologyProviderService;
39import org.slf4j.Logger;
40
Pavlin Radoslavova0e47542014-10-17 19:22:17 -070041import java.util.Collections;
tomcbff9392014-09-10 00:45:23 -070042import java.util.List;
43import java.util.Timer;
44import java.util.concurrent.ExecutorService;
45
46import static java.util.concurrent.Executors.newFixedThreadPool;
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -080047import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID;
tomcbff9392014-09-10 00:45:23 -070048import static org.onlab.onos.net.device.DeviceEvent.Type.*;
49import static org.onlab.util.Tools.namedThreads;
50import static org.slf4j.LoggerFactory.getLogger;
51
52/**
tom578ebdc2014-09-11 11:12:51 -070053 * Default implementation of a network topology provider that feeds off
54 * device and link subsystem events to trigger assembly and computation of
55 * new topology snapshots.
tomcbff9392014-09-10 00:45:23 -070056 */
57@Component(immediate = true)
Thomas Vachuska0e752bd2014-10-22 22:33:41 -070058@Service
tom97937552014-09-11 10:48:42 -070059public class DefaultTopologyProvider extends AbstractProvider
tomcbff9392014-09-10 00:45:23 -070060 implements TopologyProvider {
61
62 // TODO: make these configurable
63 private static final int MAX_EVENTS = 100;
Thomas Vachuska10ac63b2014-11-07 15:33:22 -080064 private static final int MAX_IDLE_MS = 5;
65 private static final int MAX_BATCH_MS = 50;
tomcbff9392014-09-10 00:45:23 -070066 private static final int MAX_THREADS = 8;
67
tom025e09f2014-09-15 15:29:24 -070068 // FIXME: Replace with a system-wide timer instance;
69 // TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
tomcbff9392014-09-10 00:45:23 -070070 private static final Timer TIMER = new Timer();
71
72 private final Logger log = getLogger(getClass());
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected TopologyProviderRegistry providerRegistry;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected DeviceService deviceService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected LinkService linkService;
82
83 private volatile boolean isStarted = false;
84
85 private TopologyProviderService providerService;
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -070086 private DeviceListener deviceListener = new InternalDeviceListener();
87 private LinkListener linkListener = new InternalLinkListener();
tomcbff9392014-09-10 00:45:23 -070088
89 private EventAccumulator accumulator;
90 private ExecutorService executor;
91
92 /**
93 * Creates a provider with the supplier identifier.
94 */
tom97937552014-09-11 10:48:42 -070095 public DefaultTopologyProvider() {
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -080096 super(CORE_PROVIDER_ID);
tomcbff9392014-09-10 00:45:23 -070097 }
98
99 @Activate
100 public synchronized void activate() {
tom578ebdc2014-09-11 11:12:51 -0700101 executor = newFixedThreadPool(MAX_THREADS, namedThreads("topo-build-%d"));
tomcbff9392014-09-10 00:45:23 -0700102 accumulator = new TopologyChangeAccumulator();
103
104 providerService = providerRegistry.register(this);
105 deviceService.addListener(deviceListener);
106 linkService.addListener(linkListener);
107
108 isStarted = true;
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700109 triggerRecompute();
tomcbff9392014-09-10 00:45:23 -0700110 log.info("Started");
111 }
112
113 @Deactivate
114 public synchronized void deactivate() {
tome52ce702014-09-11 00:12:54 -0700115 isStarted = false;
116
tomcbff9392014-09-10 00:45:23 -0700117 deviceService.removeListener(deviceListener);
118 linkService.removeListener(linkListener);
119 providerRegistry.unregister(this);
120 providerService = null;
121
122 executor.shutdownNow();
123 executor = null;
124
tomcbff9392014-09-10 00:45:23 -0700125 log.info("Stopped");
126 }
127
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700128 @Override
129 public void triggerRecompute() {
130 triggerTopologyBuild(Collections.<Event>emptyList());
131 }
132
tomcbff9392014-09-10 00:45:23 -0700133 /**
134 * Triggers assembly of topology data citing the specified events as the
135 * reason.
136 *
137 * @param reasons events which triggered the topology change
138 */
tome52ce702014-09-11 00:12:54 -0700139 private synchronized void triggerTopologyBuild(List<Event> reasons) {
tom97937552014-09-11 10:48:42 -0700140 if (executor != null) {
141 executor.execute(new TopologyBuilderTask(reasons));
142 }
tomcbff9392014-09-10 00:45:23 -0700143 }
144
145 // Builds the topology using the latest device and link information
146 // and citing the specified events as reasons for the change.
147 private void buildTopology(List<Event> reasons) {
tomcbff9392014-09-10 00:45:23 -0700148 if (isStarted) {
tom97937552014-09-11 10:48:42 -0700149 GraphDescription desc =
150 new DefaultGraphDescription(System.nanoTime(),
151 deviceService.getDevices(),
152 linkService.getLinks());
tomcbff9392014-09-10 00:45:23 -0700153 providerService.topologyChanged(desc, reasons);
154 }
155 }
156
157 // Callback for device events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700158 private class InternalDeviceListener implements DeviceListener {
tomcbff9392014-09-10 00:45:23 -0700159 @Override
160 public void event(DeviceEvent event) {
161 DeviceEvent.Type type = event.type();
162 if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
163 type == DEVICE_AVAILABILITY_CHANGED) {
164 accumulator.add(event);
165 }
166 }
167 }
168
169 // Callback for link events
Ayaka Koshibe3de43ca2014-09-26 16:40:23 -0700170 private class InternalLinkListener implements LinkListener {
tomcbff9392014-09-10 00:45:23 -0700171 @Override
172 public void event(LinkEvent event) {
173 accumulator.add(event);
174 }
175 }
176
177 // Event accumulator for paced triggering of topology assembly.
178 private class TopologyChangeAccumulator
179 extends AbstractEventAccumulator implements EventAccumulator {
180
181 TopologyChangeAccumulator() {
182 super(TIMER, MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
183 }
184
185 @Override
186 public void processEvents(List<Event> events) {
187 triggerTopologyBuild(events);
188 }
189
190 }
191
192 // Task for building topology data in a separate thread.
193 private class TopologyBuilderTask implements Runnable {
194 private final List<Event> reasons;
195
196 public TopologyBuilderTask(List<Event> reasons) {
197 this.reasons = reasons;
198 }
199
200 @Override
201 public void run() {
Thomas Vachuska0e752bd2014-10-22 22:33:41 -0700202 try {
203 buildTopology(reasons);
204 } catch (Exception e) {
205 log.warn("Unable to compute topology due to: {}", e.getMessage());
206 }
tomcbff9392014-09-10 00:45:23 -0700207 }
208 }
209
210}