blob: 3a6b7e387829e41376eeb1782c6b46529bcea5f2 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.provider.lldp.impl;
alshabib7911a052014-10-16 17:49:37 -070017
Marc De Leenheer0bfc2a12016-02-02 22:46:27 -080018import com.google.common.collect.ImmutableMap;
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Maps;
alshabib7911a052014-10-16 17:49:37 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080024import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
alshabib7911a052014-10-16 17:49:37 -070026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080028import org.onlab.packet.Ethernet;
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -080029import org.onlab.util.Tools;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070030import org.onosproject.cfg.ComponentConfigService;
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -080031import org.onosproject.cluster.ClusterMetadata;
Ayaka Koshibe12c8c082015-12-08 12:48:46 -080032import org.onosproject.cluster.ClusterMetadataService;
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -070033import org.onosproject.cluster.ClusterService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080034import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.mastership.MastershipEvent;
37import org.onosproject.mastership.MastershipListener;
38import org.onosproject.mastership.MastershipService;
39import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070042import org.onosproject.net.LinkKey;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.Port;
Naoki Shiota399a0b32015-11-15 20:36:13 -060044import org.onosproject.net.config.ConfigFactory;
45import org.onosproject.net.config.NetworkConfigEvent;
46import org.onosproject.net.config.NetworkConfigListener;
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -070047import org.onosproject.net.config.NetworkConfigRegistry;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.device.DeviceEvent;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080049import org.onosproject.net.device.DeviceEvent.Type;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.device.DeviceListener;
51import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080052import org.onosproject.net.flow.DefaultTrafficSelector;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080053import org.onosproject.net.flow.TrafficSelector;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070054import org.onosproject.net.link.DefaultLinkDescription;
Brian O'Connorabafb502014-12-02 22:26:20 -080055import org.onosproject.net.link.LinkProvider;
56import org.onosproject.net.link.LinkProviderRegistry;
57import org.onosproject.net.link.LinkProviderService;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070058import org.onosproject.net.link.LinkService;
Brian O'Connorabafb502014-12-02 22:26:20 -080059import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080060import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080061import org.onosproject.net.packet.PacketProcessor;
62import org.onosproject.net.packet.PacketService;
63import org.onosproject.net.provider.AbstractProvider;
64import org.onosproject.net.provider.ProviderId;
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -080065import org.onosproject.store.service.ConsistentMapException;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080066import org.osgi.service.component.ComponentContext;
alshabib7911a052014-10-16 17:49:37 -070067import org.slf4j.Logger;
68
Marc De Leenheer0bfc2a12016-02-02 22:46:27 -080069import java.util.Dictionary;
70import java.util.EnumSet;
71import java.util.Map;
72import java.util.Optional;
73import java.util.Properties;
74import java.util.Set;
75import java.util.concurrent.ConcurrentHashMap;
76import java.util.concurrent.ScheduledExecutorService;
77
78import static com.google.common.base.Strings.isNullOrEmpty;
79import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
80import static java.util.concurrent.TimeUnit.SECONDS;
81import static org.onlab.packet.Ethernet.TYPE_BSN;
82import static org.onlab.packet.Ethernet.TYPE_LLDP;
83import static org.onlab.util.Tools.get;
84import static org.onlab.util.Tools.groupedThreads;
85import static org.onosproject.cluster.ClusterMetadata.NO_NAME;
86import static org.onosproject.net.Link.Type.DIRECT;
87import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
88import static org.onosproject.net.config.basics.SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY;
89import static org.onosproject.net.config.basics.SubjectFactories.DEVICE_SUBJECT_FACTORY;
90import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080091
alshabib7911a052014-10-16 17:49:37 -070092/**
Thomas Vachuska05453c92015-09-09 14:40:49 -070093 * Provider which uses LLDP and BDDP packets to detect network infrastructure links.
alshabib7911a052014-10-16 17:49:37 -070094 */
95@Component(immediate = true)
Jonathan Hartb35540a2015-11-17 09:30:56 -080096public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
alshabib7911a052014-10-16 17:49:37 -070097
Thomas Vachuskafc52fec2015-05-18 19:13:56 -070098 private static final String PROVIDER_NAME = "org.onosproject.provider.lldp";
99
Thomas Vachuska05453c92015-09-09 14:40:49 -0700100 private static final String FORMAT =
101 "Settings: enabled={}, useBDDP={}, probeRate={}, " +
Naoki Shiota399a0b32015-11-15 20:36:13 -0600102 "staleLinkAge={}";
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800103
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700104 // When a Device/Port has this annotation, do not send out LLDP/BDDP
105 public static final String NO_LLDP = "no-lldp";
106
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -0800107 private static final int MAX_RETRIES = 5;
108 private static final int RETRY_DELAY = 1_000; // millis
109
alshabib7911a052014-10-16 17:49:37 -0700110 private final Logger log = getLogger(getClass());
111
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected CoreService coreService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib7911a052014-10-16 17:49:37 -0700116 protected LinkProviderRegistry providerRegistry;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected DeviceService deviceService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700122 protected LinkService linkService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800125 protected PacketService packetService;
alshabib7911a052014-10-16 17:49:37 -0700126
alshabib875d6262014-10-17 16:19:40 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected MastershipService masterService;
129
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected ComponentConfigService cfgService;
132
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected ClusterService clusterService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected NetworkConfigRegistry cfgRegistry;
138
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected ClusterMetadataService clusterMetadataService;
141
alshabib7911a052014-10-16 17:49:37 -0700142 private LinkProviderService providerService;
143
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800144 private ScheduledExecutorService executor;
145
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700146 // TODO: Add sanity checking for the configurable params based on the delays
147 private static final long DEVICE_SYNC_DELAY = 5;
148 private static final long LINK_PRUNER_DELAY = 3;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700149
150 private static final String PROP_ENABLED = "enabled";
151 @Property(name = PROP_ENABLED, boolValue = true,
152 label = "If false, link discovery is disabled")
153 private boolean enabled = false;
154
155 private static final String PROP_USE_BDDP = "useBDDP";
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700156 @Property(name = PROP_USE_BDDP, boolValue = true,
157 label = "Use BDDP for link discovery")
Jonathan Hartb35540a2015-11-17 09:30:56 -0800158 private boolean useBddp = true;
alshabib7911a052014-10-16 17:49:37 -0700159
Thomas Vachuska05453c92015-09-09 14:40:49 -0700160 private static final String PROP_PROBE_RATE = "probeRate";
Thomas Vachuska9e51fd02016-01-04 16:51:28 -0800161 private static final int DEFAULT_PROBE_RATE = 3000;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700162 @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
163 label = "LLDP and BDDP probe rate specified in millis")
164 private int probeRate = DEFAULT_PROBE_RATE;
Saurav Dasc313c402015-02-27 10:09:47 -0800165
Thomas Vachuska05453c92015-09-09 14:40:49 -0700166 private static final String PROP_STALE_LINK_AGE = "staleLinkAge";
Thomas Vachuska9e51fd02016-01-04 16:51:28 -0800167 private static final int DEFAULT_STALE_LINK_AGE = 10000;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700168 @Property(name = PROP_STALE_LINK_AGE, intValue = DEFAULT_STALE_LINK_AGE,
169 label = "Number of millis beyond which links will be considered stale")
170 private int staleLinkAge = DEFAULT_STALE_LINK_AGE;
alshabib7911a052014-10-16 17:49:37 -0700171
Thomas Vachuska05453c92015-09-09 14:40:49 -0700172 private final DiscoveryContext context = new InternalDiscoveryContext();
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800173 private final InternalRoleListener roleListener = new InternalRoleListener();
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700174 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
175 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800176
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700177 // Device link discovery helpers.
alshabib7911a052014-10-16 17:49:37 -0700178 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
179
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700180 // Most recent time a tracked link was seen; links are tracked if their
181 // destination connection point is mastered by this controller instance.
182 private final Map<LinkKey, Long> linkTimes = Maps.newConcurrentMap();
183
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800184 private ApplicationId appId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800185
Naoki Shiota399a0b32015-11-15 20:36:13 -0600186 static final SuppressionRules DEFAULT_RULES
Marc De Leenheer0bfc2a12016-02-02 22:46:27 -0800187 = new SuppressionRules(EnumSet.of(Device.Type.ROADM, Device.Type.FIBER_SWITCH),
Naoki Shiota399a0b32015-11-15 20:36:13 -0600188 ImmutableMap.of(NO_LLDP, SuppressionRules.ANY_VALUE));
189
190 private SuppressionRules rules = LldpLinkProvider.DEFAULT_RULES;
191
192 public static final String CONFIG_KEY = "suppression";
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800193 public static final String FEATURE_NAME = "linkDiscovery";
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800194 public static final String FINGERPRINT_FEATURE_NAME = "fingerprint";
Naoki Shiota399a0b32015-11-15 20:36:13 -0600195
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800196 private final Set<ConfigFactory<?, ?>> factories = ImmutableSet.of(
Naoki Shiota399a0b32015-11-15 20:36:13 -0600197 new ConfigFactory<ApplicationId, SuppressionConfig>(APP_SUBJECT_FACTORY,
198 SuppressionConfig.class,
199 CONFIG_KEY) {
200 @Override
201 public SuppressionConfig createConfig() {
202 return new SuppressionConfig();
203 }
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800204 },
205 new ConfigFactory<DeviceId, LinkDiscoveryFromDevice>(DEVICE_SUBJECT_FACTORY,
206 LinkDiscoveryFromDevice.class, FEATURE_NAME) {
207 @Override
208 public LinkDiscoveryFromDevice createConfig() {
209 return new LinkDiscoveryFromDevice();
210 }
211 },
212 new ConfigFactory<ConnectPoint, LinkDiscoveryFromPort>(CONNECT_POINT_SUBJECT_FACTORY,
213 LinkDiscoveryFromPort.class, FEATURE_NAME) {
214 @Override
215 public LinkDiscoveryFromPort createConfig() {
216 return new LinkDiscoveryFromPort();
217 }
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800218 },
219 new ConfigFactory<DeviceId, FingerprintProbeFromDevice>(DEVICE_SUBJECT_FACTORY,
220 FingerprintProbeFromDevice.class, FINGERPRINT_FEATURE_NAME) {
221 @Override
222 public FingerprintProbeFromDevice createConfig() {
223 return new FingerprintProbeFromDevice();
224 }
Naoki Shiota399a0b32015-11-15 20:36:13 -0600225 }
226 );
227
228 private final InternalConfigListener cfgListener = new InternalConfigListener();
229
230
alshabib7911a052014-10-16 17:49:37 -0700231 /**
232 * Creates an OpenFlow link provider.
233 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800234 public LldpLinkProvider() {
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700235 super(new ProviderId("lldp", PROVIDER_NAME));
alshabib7911a052014-10-16 17:49:37 -0700236 }
237
238 @Activate
Saurav Dasc313c402015-02-27 10:09:47 -0800239 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700240 cfgService.registerProperties(getClass());
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700241 appId = coreService.registerApplication(PROVIDER_NAME);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600242
243 cfgRegistry.addListener(cfgListener);
244 factories.forEach(cfgRegistry::registerConfigFactory);
245
Thomas Vachuska762a2d82016-01-04 10:25:20 -0800246 SuppressionConfig cfg =
247 Tools.retryable(() -> cfgRegistry.getConfig(appId, SuppressionConfig.class),
248 ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY).get();
Naoki Shiota399a0b32015-11-15 20:36:13 -0600249 if (cfg == null) {
250 // If no configuration is found, register default.
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -0800251 cfg = Tools.retryable(this::setDefaultSuppressionConfig,
252 ConsistentMapException.class,
253 MAX_RETRIES, RETRY_DELAY).get();
Naoki Shiota399a0b32015-11-15 20:36:13 -0600254 }
255 cfgListener.reconfigureSuppressionRules(cfg);
256
Saurav Dasc313c402015-02-27 10:09:47 -0800257 modified(context);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700258 log.info("Started");
259 }
260
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -0800261 private SuppressionConfig setDefaultSuppressionConfig() {
262 SuppressionConfig cfg = cfgRegistry.addConfig(appId, SuppressionConfig.class);
263 cfg.deviceTypes(DEFAULT_RULES.getSuppressedDeviceType())
264 .annotation(DEFAULT_RULES.getSuppressedAnnotation())
265 .apply();
266 return cfg;
267 }
268
Thomas Vachuska05453c92015-09-09 14:40:49 -0700269 @Deactivate
270 public void deactivate() {
Naoki Shiota399a0b32015-11-15 20:36:13 -0600271 cfgRegistry.removeListener(cfgListener);
272 factories.forEach(cfgRegistry::unregisterConfigFactory);
273
Thomas Vachuska05453c92015-09-09 14:40:49 -0700274 cfgService.unregisterProperties(getClass(), false);
275 disable();
276 log.info("Stopped");
277 }
278
279 @Modified
280 public void modified(ComponentContext context) {
281 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
282
283 boolean newEnabled, newUseBddp;
284 int newProbeRate, newStaleLinkAge;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700285 try {
286 String s = get(properties, PROP_ENABLED);
287 newEnabled = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
288
289 s = get(properties, PROP_USE_BDDP);
290 newUseBddp = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
291
292 s = get(properties, PROP_PROBE_RATE);
293 newProbeRate = isNullOrEmpty(s) ? probeRate : Integer.parseInt(s.trim());
294
295 s = get(properties, PROP_STALE_LINK_AGE);
296 newStaleLinkAge = isNullOrEmpty(s) ? staleLinkAge : Integer.parseInt(s.trim());
297
Thomas Vachuska05453c92015-09-09 14:40:49 -0700298 } catch (NumberFormatException e) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700299 log.warn("Component configuration had invalid values", e);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700300 newEnabled = enabled;
Jonathan Hartb35540a2015-11-17 09:30:56 -0800301 newUseBddp = useBddp;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700302 newProbeRate = probeRate;
303 newStaleLinkAge = staleLinkAge;
Saurav Dasc313c402015-02-27 10:09:47 -0800304 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800305
Thomas Vachuska05453c92015-09-09 14:40:49 -0700306 boolean wasEnabled = enabled;
307
308 enabled = newEnabled;
Jonathan Hartb35540a2015-11-17 09:30:56 -0800309 useBddp = newUseBddp;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700310 probeRate = newProbeRate;
311 staleLinkAge = newStaleLinkAge;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700312
313 if (!wasEnabled && enabled) {
314 enable();
315 } else if (wasEnabled && !enabled) {
316 disable();
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700317 } else {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700318 if (enabled) {
319 // update all discovery helper state
320 loadDevices();
321 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700322 }
323
Naoki Shiota399a0b32015-11-15 20:36:13 -0600324 log.info(FORMAT, enabled, useBddp, probeRate, staleLinkAge);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700325 }
326
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700327 /**
328 * Enables link discovery processing.
329 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700330 private void enable() {
alshabib7911a052014-10-16 17:49:37 -0700331 providerService = providerRegistry.register(this);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800332 masterService.addListener(roleListener);
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700333 deviceService.addListener(deviceListener);
334 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800335
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700336 loadDevices();
Thomas Vachuska05453c92015-09-09 14:40:49 -0700337
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700338 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/link", "discovery-%d"));
339 executor.scheduleAtFixedRate(new SyncDeviceInfoTask(),
340 DEVICE_SYNC_DELAY, DEVICE_SYNC_DELAY, SECONDS);
341 executor.scheduleAtFixedRate(new LinkPrunerTask(),
342 LINK_PRUNER_DELAY, LINK_PRUNER_DELAY, SECONDS);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700343
Thomas Vachuska05453c92015-09-09 14:40:49 -0700344 requestIntercepts();
345 }
346
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700347 /**
348 * Disables link discovery processing.
349 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700350 private void disable() {
351 withdrawIntercepts();
352
353 providerRegistry.unregister(this);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700354 masterService.removeListener(roleListener);
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700355 deviceService.removeListener(deviceListener);
356 packetService.removeProcessor(packetProcessor);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700357
Naoki Shiota399a0b32015-11-15 20:36:13 -0600358
Thomas Vachuska05453c92015-09-09 14:40:49 -0700359 if (executor != null) {
360 executor.shutdownNow();
361 }
362 discoverers.values().forEach(LinkDiscovery::stop);
363 discoverers.clear();
364
365 providerService = null;
366 }
367
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700368 /**
369 * Loads available devices and registers their ports to be probed.
370 */
371 private void loadDevices() {
Naoki Shiota399a0b32015-11-15 20:36:13 -0600372 if (!enabled) {
373 return;
374 }
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700375 deviceService.getAvailableDevices()
376 .forEach(d -> updateDevice(d)
377 .ifPresent(ld -> updatePorts(ld, d.id())));
378 }
379
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800380 private boolean isBlacklisted(DeviceId did) {
381 LinkDiscoveryFromDevice cfg = cfgRegistry.getConfig(did, LinkDiscoveryFromDevice.class);
382 if (cfg == null) {
383 return false;
384 }
385 return !cfg.enabled();
386 }
387
388 private boolean isBlacklisted(ConnectPoint cp) {
389 // if parent device is blacklisted, so is the port
390 if (isBlacklisted(cp.deviceId())) {
391 return true;
392 }
393 LinkDiscoveryFromPort cfg = cfgRegistry.getConfig(cp, LinkDiscoveryFromPort.class);
394 if (cfg == null) {
395 return false;
396 }
397 return !cfg.enabled();
398 }
399
400 private boolean isBlacklisted(Port port) {
401 return isBlacklisted(new ConnectPoint(port.element().id(), port.number()));
402 }
403
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800404 private boolean isFingerprinted(DeviceId did) {
405 FingerprintProbeFromDevice cfg = cfgRegistry.getConfig(did, FingerprintProbeFromDevice.class);
406 if (cfg == null) {
407 return false;
408 }
409 return cfg.enabled();
410 }
411
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700412 /**
413 * Updates discovery helper for specified device.
414 *
415 * Adds and starts a discovery helper for specified device if enabled,
416 * calls {@link #removeDevice(DeviceId)} otherwise.
417 *
418 * @param device device to add
419 * @return discovery helper if discovery is enabled for the device
420 */
421 private Optional<LinkDiscovery> updateDevice(Device device) {
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800422 if (device == null) {
423 return Optional.empty();
424 }
425 if (rules.isSuppressed(device) || isBlacklisted(device.id())) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700426 log.trace("LinkDiscovery from {} disabled by configuration", device.id());
427 removeDevice(device.id());
428 return Optional.empty();
429 }
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800430
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700431 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
432 did -> new LinkDiscovery(device, context));
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800433 if (isFingerprinted(device.id())) {
434 ld.enableFingerprint();
435 } else {
436 ld.disableFingerprint();
437 }
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700438 if (ld.isStopped()) {
439 ld.start();
440 }
441 return Optional.of(ld);
442 }
443
444 /**
445 * Removes after stopping discovery helper for specified device.
446 * @param deviceId device to remove
447 */
448 private void removeDevice(final DeviceId deviceId) {
449 discoverers.computeIfPresent(deviceId, (did, ld) -> {
450 ld.stop();
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700451 return null;
452 });
453
454 }
455
456 /**
457 * Updates ports of the specified device to the specified discovery helper.
458 */
459 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
460 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
461 }
462
463 /**
464 * Updates discovery helper state of the specified port.
465 *
466 * Adds a port to the discovery helper if up and discovery is enabled,
467 * or calls {@link #removePort(Port)} otherwise.
468 */
469 private void updatePort(LinkDiscovery discoverer, Port port) {
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800470 if (port == null) {
471 return;
472 }
HIGUCHI Yutab853b3f2015-11-17 18:43:20 -0800473 if (port.number().isLogical()) {
474 // silently ignore logical ports
475 return;
476 }
Naoki Shiota399a0b32015-11-15 20:36:13 -0600477
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800478 if (rules.isSuppressed(port) || isBlacklisted(port)) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700479 log.trace("LinkDiscovery from {} disabled by configuration", port);
480 removePort(port);
481 return;
482 }
483
484 // check if enabled and turn off discovery?
485 if (!port.isEnabled()) {
486 removePort(port);
487 return;
488 }
489
HIGUCHI Yutab853b3f2015-11-17 18:43:20 -0800490 discoverer.addPort(port);
alshabib7911a052014-10-16 17:49:37 -0700491 }
492
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700493 /**
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700494 * Removes a port from the specified discovery helper.
495 * @param port the port
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700496 */
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700497 private void removePort(Port port) {
498 if (port.element() instanceof Device) {
499 Device d = (Device) port.element();
500 LinkDiscovery ld = discoverers.get(d.id());
501 if (ld != null) {
502 ld.removePort(port.number());
Jonathan Hart45066bc2015-07-28 11:18:34 -0700503 }
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700504 } else {
505 log.warn("Attempted to remove non-Device port", port);
Jonathan Hart45066bc2015-07-28 11:18:34 -0700506 }
507 }
508
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700509 /**
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700510 * Requests packet intercepts.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800511 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700512 private void requestIntercepts() {
513 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Thomas Vachuska347cc872015-09-23 10:25:29 -0700514 selector.matchEthType(TYPE_LLDP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700515 packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800516
Thomas Vachuska347cc872015-09-23 10:25:29 -0700517 selector.matchEthType(TYPE_BSN);
Jonathan Hartb35540a2015-11-17 09:30:56 -0800518 if (useBddp) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700519 packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
520 } else {
521 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800522 }
523 }
524
Thomas Vachuska27bee092015-06-23 19:03:10 -0700525 /**
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700526 * Withdraws packet intercepts.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700527 */
528 private void withdrawIntercepts() {
529 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Thomas Vachuska347cc872015-09-23 10:25:29 -0700530 selector.matchEthType(TYPE_LLDP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700531 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
Thomas Vachuska347cc872015-09-23 10:25:29 -0700532 selector.matchEthType(TYPE_BSN);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700533 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
534 }
535
Naoki Shiota399a0b32015-11-15 20:36:13 -0600536 protected SuppressionRules rules() {
537 return rules;
538 }
539
540 protected void updateRules(SuppressionRules newRules) {
541 if (!rules.equals(newRules)) {
542 rules = newRules;
543 loadDevices();
544 }
545 }
546
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700547 /**
548 * Processes device mastership role changes.
549 */
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800550 private class InternalRoleListener implements MastershipListener {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800551 @Override
552 public void event(MastershipEvent event) {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800553 if (MastershipEvent.Type.BACKUPS_CHANGED.equals(event.type())) {
554 // only need new master events
555 return;
556 }
557
558 DeviceId deviceId = event.subject();
559 Device device = deviceService.getDevice(deviceId);
560 if (device == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700561 log.debug("Device {} doesn't exist, or isn't there yet", deviceId);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800562 return;
563 }
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700564 if (clusterService.getLocalNode().id().equals(event.roleInfo().master())) {
565 updateDevice(device).ifPresent(ld -> updatePorts(ld, device.id()));
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800566 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800567 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800568 }
alshabib7911a052014-10-16 17:49:37 -0700569
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700570 /**
571 * Processes device events.
572 */
573 private class InternalDeviceListener implements DeviceListener {
alshabib7911a052014-10-16 17:49:37 -0700574 @Override
575 public void event(DeviceEvent event) {
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800576 if (event.type() == Type.PORT_STATS_UPDATED) {
577 return;
578 }
alshabib7911a052014-10-16 17:49:37 -0700579 Device device = event.subject();
alshabibacd91832014-10-17 14:38:41 -0700580 Port port = event.port();
alshabibdfc7afb2014-10-21 20:13:27 -0700581 if (device == null) {
582 log.error("Device is null.");
583 return;
584 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700585 log.trace("{} {} {}", event.type(), event.subject(), event);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700586 final DeviceId deviceId = device.id();
alshabib7911a052014-10-16 17:49:37 -0700587 switch (event.type()) {
588 case DEVICE_ADDED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700589 case DEVICE_UPDATED:
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700590 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
alshabib7911a052014-10-16 17:49:37 -0700591 break;
592 case PORT_ADDED:
593 case PORT_UPDATED:
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700594 if (port.isEnabled()) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700595 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
alshabib7911a052014-10-16 17:49:37 -0700596 } else {
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700597 log.debug("Port down {}", port);
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700598 removePort(port);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600599 providerService.linksVanished(new ConnectPoint(port.element().id(),
600 port.number()));
alshabib7911a052014-10-16 17:49:37 -0700601 }
602 break;
603 case PORT_REMOVED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700604 log.debug("Port removed {}", port);
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700605 removePort(port);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600606 providerService.linksVanished(new ConnectPoint(port.element().id(),
607 port.number()));
alshabib7911a052014-10-16 17:49:37 -0700608 break;
609 case DEVICE_REMOVED:
610 case DEVICE_SUSPENDED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700611 log.debug("Device removed {}", deviceId);
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700612 removeDevice(deviceId);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600613 providerService.linksVanished(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700614 break;
615 case DEVICE_AVAILABILITY_CHANGED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700616 if (deviceService.isAvailable(deviceId)) {
617 log.debug("Device up {}", deviceId);
alshabibe3af2652015-12-01 23:05:34 -0800618 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
alshabib7911a052014-10-16 17:49:37 -0700619 } else {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700620 log.debug("Device down {}", deviceId);
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700621 removeDevice(deviceId);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600622 providerService.linksVanished(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700623 }
624 break;
Jonathan Hart9de692c2015-04-23 11:45:47 -0700625 case PORT_STATS_UPDATED:
626 break;
alshabib7911a052014-10-16 17:49:37 -0700627 default:
628 log.debug("Unknown event {}", event);
629 }
630 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700631 }
alshabib7911a052014-10-16 17:49:37 -0700632
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700633 /**
634 * Processes incoming packets.
635 */
636 private class InternalPacketProcessor implements PacketProcessor {
alshabib7911a052014-10-16 17:49:37 -0700637 @Override
638 public void process(PacketContext context) {
Thomas Vachuska347cc872015-09-23 10:25:29 -0700639 if (context == null || context.isHandled()) {
alshabib4a179dc2014-10-17 17:17:01 -0700640 return;
641 }
Thomas Vachuska347cc872015-09-23 10:25:29 -0700642
643 Ethernet eth = context.inPacket().parsed();
644 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
645 return;
646 }
647
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700648 LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId());
alshabib7911a052014-10-16 17:49:37 -0700649 if (ld == null) {
650 return;
651 }
652
Jonathan Hartb35540a2015-11-17 09:30:56 -0800653 if (ld.handleLldp(context)) {
alshabib7911a052014-10-16 17:49:37 -0700654 context.block();
655 }
656 }
657 }
658
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700659 /**
660 * Auxiliary task to keep device ports up to date.
661 */
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800662 private final class SyncDeviceInfoTask implements Runnable {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800663 @Override
664 public void run() {
665 if (Thread.currentThread().isInterrupted()) {
666 log.info("Interrupted, quitting");
667 return;
668 }
669 // check what deviceService sees, to see if we are missing anything
670 try {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700671 loadDevices();
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800672 } catch (Exception e) {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700673 // Catch all exceptions to avoid task being suppressed
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800674 log.error("Exception thrown during synchronization process", e);
675 }
676 }
677 }
678
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700679 /**
680 * Auxiliary task for pruning stale links.
681 */
682 private class LinkPrunerTask implements Runnable {
683 @Override
684 public void run() {
685 if (Thread.currentThread().isInterrupted()) {
686 log.info("Interrupted, quitting");
687 return;
688 }
689
690 try {
691 // TODO: There is still a slight possibility of mastership
692 // change occurring right with link going stale. This will
693 // result in the stale link not being pruned.
694 Maps.filterEntries(linkTimes, e -> {
695 if (!masterService.isLocalMaster(e.getKey().dst().deviceId())) {
696 return true;
697 }
698 if (isStale(e.getValue())) {
699 providerService.linkVanished(new DefaultLinkDescription(e.getKey().src(),
700 e.getKey().dst(),
701 DIRECT));
702 return true;
703 }
704 return false;
705 }).clear();
706
707 } catch (Exception e) {
708 // Catch all exceptions to avoid task being suppressed
709 log.error("Exception thrown during link pruning process", e);
710 }
711 }
712
713 private boolean isStale(long lastSeen) {
714 return lastSeen < System.currentTimeMillis() - staleLinkAge;
715 }
716 }
717
718 /**
719 * Provides processing context for the device link discovery helpers.
720 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700721 private class InternalDiscoveryContext implements DiscoveryContext {
722 @Override
723 public MastershipService mastershipService() {
724 return masterService;
725 }
726
727 @Override
728 public LinkProviderService providerService() {
729 return providerService;
730 }
731
732 @Override
733 public PacketService packetService() {
734 return packetService;
735 }
736
737 @Override
738 public long probeRate() {
739 return probeRate;
740 }
741
742 @Override
Jonathan Hartb35540a2015-11-17 09:30:56 -0800743 public boolean useBddp() {
744 return useBddp;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700745 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700746
747 @Override
748 public void touchLink(LinkKey key) {
749 linkTimes.put(key, System.currentTimeMillis());
750 }
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800751
752 @Override
753 public String fingerprint() {
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800754 ClusterMetadata mdata = clusterMetadataService.getClusterMetadata();
755 return mdata == null ? NO_NAME : mdata.getName();
756 }
757
758 @Override
759 public DeviceService deviceService() {
760 return deviceService;
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800761 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700762 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700763
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800764 static final EnumSet<NetworkConfigEvent.Type> CONFIG_CHANGED
765 = EnumSet.of(NetworkConfigEvent.Type.CONFIG_ADDED,
766 NetworkConfigEvent.Type.CONFIG_UPDATED,
767 NetworkConfigEvent.Type.CONFIG_REMOVED);
768
Naoki Shiota399a0b32015-11-15 20:36:13 -0600769 private class InternalConfigListener implements NetworkConfigListener {
770
771 private synchronized void reconfigureSuppressionRules(SuppressionConfig cfg) {
772 if (cfg == null) {
773 log.error("Suppression Config is null.");
774 return;
775 }
776
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800777 SuppressionRules newRules = new SuppressionRules(cfg.deviceTypes(),
Naoki Shiota399a0b32015-11-15 20:36:13 -0600778 cfg.annotation());
779
780 updateRules(newRules);
781 }
782
783 @Override
784 public void event(NetworkConfigEvent event) {
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800785 if (event.configClass() == LinkDiscoveryFromDevice.class &&
786 CONFIG_CHANGED.contains(event.type())) {
787
788 if (event.subject() instanceof DeviceId) {
789 final DeviceId did = (DeviceId) event.subject();
790 Device device = deviceService.getDevice(did);
791 updateDevice(device).ifPresent(ld -> updatePorts(ld, did));
792 }
793
794 } else if (event.configClass() == LinkDiscoveryFromPort.class &&
795 CONFIG_CHANGED.contains(event.type())) {
796
797 if (event.subject() instanceof ConnectPoint) {
798 ConnectPoint cp = (ConnectPoint) event.subject();
799 if (cp.elementId() instanceof DeviceId) {
800 final DeviceId did = (DeviceId) cp.elementId();
801 Device device = deviceService.getDevice(did);
802 Port port = deviceService.getPort(did, cp.port());
803 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
804 }
805 }
806
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800807 } else if (event.configClass() == FingerprintProbeFromDevice.class &&
808 CONFIG_CHANGED.contains(event.type())) {
809
810 if (event.subject() instanceof DeviceId) {
811 final DeviceId did = (DeviceId) event.subject();
812 Device device = deviceService.getDevice(did);
813 updateDevice(device);
814 }
815
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800816 } else if (event.configClass().equals(SuppressionConfig.class) &&
Naoki Shiota399a0b32015-11-15 20:36:13 -0600817 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
818 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) {
819 SuppressionConfig cfg = cfgRegistry.getConfig(appId, SuppressionConfig.class);
820 reconfigureSuppressionRules(cfg);
821 log.trace("Network config reconfigured");
822 }
823 }
824 }
alshabib7911a052014-10-16 17:49:37 -0700825}