blob: 8c2450231bc59019009fc241a606ae15c609aac9 [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
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -080018import static com.google.common.base.Strings.isNullOrEmpty;
19import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
20import static java.util.concurrent.TimeUnit.SECONDS;
21import static org.onlab.packet.Ethernet.TYPE_BSN;
22import static org.onlab.packet.Ethernet.TYPE_LLDP;
23import static org.onlab.util.Tools.get;
24import static org.onlab.util.Tools.groupedThreads;
25import static org.onosproject.net.Link.Type.DIRECT;
26import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
27import static org.onosproject.net.config.basics.SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY;
28import static org.onosproject.net.config.basics.SubjectFactories.DEVICE_SUBJECT_FACTORY;
29import static org.slf4j.LoggerFactory.getLogger;
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -080030import static org.onosproject.cluster.ClusterMetadata.NO_NAME;
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -080031
32import java.util.Dictionary;
33import java.util.EnumSet;
34import java.util.Map;
35import java.util.Optional;
36import java.util.Properties;
37import java.util.Set;
38import java.util.concurrent.ConcurrentHashMap;
39import java.util.concurrent.ScheduledExecutorService;
40
alshabib7911a052014-10-16 17:49:37 -070041import org.apache.felix.scr.annotations.Activate;
42import org.apache.felix.scr.annotations.Component;
43import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080044import org.apache.felix.scr.annotations.Modified;
45import org.apache.felix.scr.annotations.Property;
alshabib7911a052014-10-16 17:49:37 -070046import org.apache.felix.scr.annotations.Reference;
47import org.apache.felix.scr.annotations.ReferenceCardinality;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080048import org.onlab.packet.Ethernet;
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -080049import org.onlab.util.Tools;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070050import org.onosproject.cfg.ComponentConfigService;
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -080051import org.onosproject.cluster.ClusterMetadata;
Ayaka Koshibe12c8c082015-12-08 12:48:46 -080052import org.onosproject.cluster.ClusterMetadataService;
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -070053import org.onosproject.cluster.ClusterService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080054import org.onosproject.core.ApplicationId;
55import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080056import org.onosproject.mastership.MastershipEvent;
57import org.onosproject.mastership.MastershipListener;
58import org.onosproject.mastership.MastershipService;
59import org.onosproject.net.ConnectPoint;
60import org.onosproject.net.Device;
61import org.onosproject.net.DeviceId;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070062import org.onosproject.net.LinkKey;
Brian O'Connorabafb502014-12-02 22:26:20 -080063import org.onosproject.net.Port;
Naoki Shiota399a0b32015-11-15 20:36:13 -060064import org.onosproject.net.config.ConfigFactory;
65import org.onosproject.net.config.NetworkConfigEvent;
66import org.onosproject.net.config.NetworkConfigListener;
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -070067import org.onosproject.net.config.NetworkConfigRegistry;
Brian O'Connorabafb502014-12-02 22:26:20 -080068import org.onosproject.net.device.DeviceEvent;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080069import org.onosproject.net.device.DeviceEvent.Type;
Brian O'Connorabafb502014-12-02 22:26:20 -080070import org.onosproject.net.device.DeviceListener;
71import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080072import org.onosproject.net.flow.DefaultTrafficSelector;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080073import org.onosproject.net.flow.TrafficSelector;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070074import org.onosproject.net.link.DefaultLinkDescription;
Brian O'Connorabafb502014-12-02 22:26:20 -080075import org.onosproject.net.link.LinkProvider;
76import org.onosproject.net.link.LinkProviderRegistry;
77import org.onosproject.net.link.LinkProviderService;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070078import org.onosproject.net.link.LinkService;
Brian O'Connorabafb502014-12-02 22:26:20 -080079import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080080import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080081import org.onosproject.net.packet.PacketProcessor;
82import org.onosproject.net.packet.PacketService;
83import org.onosproject.net.provider.AbstractProvider;
84import org.onosproject.net.provider.ProviderId;
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -080085import org.onosproject.store.service.ConsistentMapException;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080086import org.osgi.service.component.ComponentContext;
alshabib7911a052014-10-16 17:49:37 -070087import org.slf4j.Logger;
88
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -080089import com.google.common.collect.ImmutableMap;
90import com.google.common.collect.ImmutableSet;
91import com.google.common.collect.Maps;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080092
alshabib7911a052014-10-16 17:49:37 -070093/**
Thomas Vachuska05453c92015-09-09 14:40:49 -070094 * Provider which uses LLDP and BDDP packets to detect network infrastructure links.
alshabib7911a052014-10-16 17:49:37 -070095 */
96@Component(immediate = true)
Jonathan Hartb35540a2015-11-17 09:30:56 -080097public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
alshabib7911a052014-10-16 17:49:37 -070098
Thomas Vachuskafc52fec2015-05-18 19:13:56 -070099 private static final String PROVIDER_NAME = "org.onosproject.provider.lldp";
100
Thomas Vachuska05453c92015-09-09 14:40:49 -0700101 private static final String FORMAT =
102 "Settings: enabled={}, useBDDP={}, probeRate={}, " +
Naoki Shiota399a0b32015-11-15 20:36:13 -0600103 "staleLinkAge={}";
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800104
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700105 // When a Device/Port has this annotation, do not send out LLDP/BDDP
106 public static final String NO_LLDP = "no-lldp";
107
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -0800108 private static final int MAX_RETRIES = 5;
109 private static final int RETRY_DELAY = 1_000; // millis
110
alshabib7911a052014-10-16 17:49:37 -0700111 private final Logger log = getLogger(getClass());
112
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected CoreService coreService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib7911a052014-10-16 17:49:37 -0700117 protected LinkProviderRegistry providerRegistry;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected DeviceService deviceService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700123 protected LinkService linkService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800126 protected PacketService packetService;
alshabib7911a052014-10-16 17:49:37 -0700127
alshabib875d6262014-10-17 16:19:40 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected MastershipService masterService;
130
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected ComponentConfigService cfgService;
133
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected ClusterService clusterService;
136
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected NetworkConfigRegistry cfgRegistry;
139
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800140 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
141 protected ClusterMetadataService clusterMetadataService;
142
alshabib7911a052014-10-16 17:49:37 -0700143 private LinkProviderService providerService;
144
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800145 private ScheduledExecutorService executor;
146
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700147 // TODO: Add sanity checking for the configurable params based on the delays
148 private static final long DEVICE_SYNC_DELAY = 5;
149 private static final long LINK_PRUNER_DELAY = 3;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700150
151 private static final String PROP_ENABLED = "enabled";
152 @Property(name = PROP_ENABLED, boolValue = true,
153 label = "If false, link discovery is disabled")
154 private boolean enabled = false;
155
156 private static final String PROP_USE_BDDP = "useBDDP";
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700157 @Property(name = PROP_USE_BDDP, boolValue = true,
158 label = "Use BDDP for link discovery")
Jonathan Hartb35540a2015-11-17 09:30:56 -0800159 private boolean useBddp = true;
alshabib7911a052014-10-16 17:49:37 -0700160
Thomas Vachuska05453c92015-09-09 14:40:49 -0700161 private static final String PROP_PROBE_RATE = "probeRate";
Thomas Vachuska9e51fd02016-01-04 16:51:28 -0800162 private static final int DEFAULT_PROBE_RATE = 3000;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700163 @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
164 label = "LLDP and BDDP probe rate specified in millis")
165 private int probeRate = DEFAULT_PROBE_RATE;
Saurav Dasc313c402015-02-27 10:09:47 -0800166
Thomas Vachuska05453c92015-09-09 14:40:49 -0700167 private static final String PROP_STALE_LINK_AGE = "staleLinkAge";
Thomas Vachuska9e51fd02016-01-04 16:51:28 -0800168 private static final int DEFAULT_STALE_LINK_AGE = 10000;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700169 @Property(name = PROP_STALE_LINK_AGE, intValue = DEFAULT_STALE_LINK_AGE,
170 label = "Number of millis beyond which links will be considered stale")
171 private int staleLinkAge = DEFAULT_STALE_LINK_AGE;
alshabib7911a052014-10-16 17:49:37 -0700172
Thomas Vachuska05453c92015-09-09 14:40:49 -0700173 private final DiscoveryContext context = new InternalDiscoveryContext();
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800174 private final InternalRoleListener roleListener = new InternalRoleListener();
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700175 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
176 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800177
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700178 // Device link discovery helpers.
alshabib7911a052014-10-16 17:49:37 -0700179 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
180
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700181 // Most recent time a tracked link was seen; links are tracked if their
182 // destination connection point is mastered by this controller instance.
183 private final Map<LinkKey, Long> linkTimes = Maps.newConcurrentMap();
184
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800185 private ApplicationId appId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800186
Naoki Shiota399a0b32015-11-15 20:36:13 -0600187 static final SuppressionRules DEFAULT_RULES
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800188 = new SuppressionRules(EnumSet.of(Device.Type.ROADM),
Naoki Shiota399a0b32015-11-15 20:36:13 -0600189 ImmutableMap.of(NO_LLDP, SuppressionRules.ANY_VALUE));
190
191 private SuppressionRules rules = LldpLinkProvider.DEFAULT_RULES;
192
193 public static final String CONFIG_KEY = "suppression";
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800194 public static final String FEATURE_NAME = "linkDiscovery";
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800195 public static final String FINGERPRINT_FEATURE_NAME = "fingerprint";
Naoki Shiota399a0b32015-11-15 20:36:13 -0600196
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800197 private final Set<ConfigFactory<?, ?>> factories = ImmutableSet.of(
Naoki Shiota399a0b32015-11-15 20:36:13 -0600198 new ConfigFactory<ApplicationId, SuppressionConfig>(APP_SUBJECT_FACTORY,
199 SuppressionConfig.class,
200 CONFIG_KEY) {
201 @Override
202 public SuppressionConfig createConfig() {
203 return new SuppressionConfig();
204 }
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800205 },
206 new ConfigFactory<DeviceId, LinkDiscoveryFromDevice>(DEVICE_SUBJECT_FACTORY,
207 LinkDiscoveryFromDevice.class, FEATURE_NAME) {
208 @Override
209 public LinkDiscoveryFromDevice createConfig() {
210 return new LinkDiscoveryFromDevice();
211 }
212 },
213 new ConfigFactory<ConnectPoint, LinkDiscoveryFromPort>(CONNECT_POINT_SUBJECT_FACTORY,
214 LinkDiscoveryFromPort.class, FEATURE_NAME) {
215 @Override
216 public LinkDiscoveryFromPort createConfig() {
217 return new LinkDiscoveryFromPort();
218 }
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800219 },
220 new ConfigFactory<DeviceId, FingerprintProbeFromDevice>(DEVICE_SUBJECT_FACTORY,
221 FingerprintProbeFromDevice.class, FINGERPRINT_FEATURE_NAME) {
222 @Override
223 public FingerprintProbeFromDevice createConfig() {
224 return new FingerprintProbeFromDevice();
225 }
Naoki Shiota399a0b32015-11-15 20:36:13 -0600226 }
227 );
228
229 private final InternalConfigListener cfgListener = new InternalConfigListener();
230
231
alshabib7911a052014-10-16 17:49:37 -0700232 /**
233 * Creates an OpenFlow link provider.
234 */
Jonathan Hartb35540a2015-11-17 09:30:56 -0800235 public LldpLinkProvider() {
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700236 super(new ProviderId("lldp", PROVIDER_NAME));
alshabib7911a052014-10-16 17:49:37 -0700237 }
238
239 @Activate
Saurav Dasc313c402015-02-27 10:09:47 -0800240 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700241 cfgService.registerProperties(getClass());
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700242 appId = coreService.registerApplication(PROVIDER_NAME);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600243
244 cfgRegistry.addListener(cfgListener);
245 factories.forEach(cfgRegistry::registerConfigFactory);
246
Thomas Vachuska762a2d82016-01-04 10:25:20 -0800247 SuppressionConfig cfg =
248 Tools.retryable(() -> cfgRegistry.getConfig(appId, SuppressionConfig.class),
249 ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY).get();
Naoki Shiota399a0b32015-11-15 20:36:13 -0600250 if (cfg == null) {
251 // If no configuration is found, register default.
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -0800252 cfg = Tools.retryable(this::setDefaultSuppressionConfig,
253 ConsistentMapException.class,
254 MAX_RETRIES, RETRY_DELAY).get();
Naoki Shiota399a0b32015-11-15 20:36:13 -0600255 }
256 cfgListener.reconfigureSuppressionRules(cfg);
257
Saurav Dasc313c402015-02-27 10:09:47 -0800258 modified(context);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700259 log.info("Started");
260 }
261
Thomas Vachuskaaad8b1d2015-12-11 10:36:53 -0800262 private SuppressionConfig setDefaultSuppressionConfig() {
263 SuppressionConfig cfg = cfgRegistry.addConfig(appId, SuppressionConfig.class);
264 cfg.deviceTypes(DEFAULT_RULES.getSuppressedDeviceType())
265 .annotation(DEFAULT_RULES.getSuppressedAnnotation())
266 .apply();
267 return cfg;
268 }
269
Thomas Vachuska05453c92015-09-09 14:40:49 -0700270 @Deactivate
271 public void deactivate() {
Naoki Shiota399a0b32015-11-15 20:36:13 -0600272 cfgRegistry.removeListener(cfgListener);
273 factories.forEach(cfgRegistry::unregisterConfigFactory);
274
Thomas Vachuska05453c92015-09-09 14:40:49 -0700275 cfgService.unregisterProperties(getClass(), false);
276 disable();
277 log.info("Stopped");
278 }
279
280 @Modified
281 public void modified(ComponentContext context) {
282 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
283
284 boolean newEnabled, newUseBddp;
285 int newProbeRate, newStaleLinkAge;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700286 try {
287 String s = get(properties, PROP_ENABLED);
288 newEnabled = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
289
290 s = get(properties, PROP_USE_BDDP);
291 newUseBddp = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
292
293 s = get(properties, PROP_PROBE_RATE);
294 newProbeRate = isNullOrEmpty(s) ? probeRate : Integer.parseInt(s.trim());
295
296 s = get(properties, PROP_STALE_LINK_AGE);
297 newStaleLinkAge = isNullOrEmpty(s) ? staleLinkAge : Integer.parseInt(s.trim());
298
Thomas Vachuska05453c92015-09-09 14:40:49 -0700299 } catch (NumberFormatException e) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700300 log.warn("Component configuration had invalid values", e);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700301 newEnabled = enabled;
Jonathan Hartb35540a2015-11-17 09:30:56 -0800302 newUseBddp = useBddp;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700303 newProbeRate = probeRate;
304 newStaleLinkAge = staleLinkAge;
Saurav Dasc313c402015-02-27 10:09:47 -0800305 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800306
Thomas Vachuska05453c92015-09-09 14:40:49 -0700307 boolean wasEnabled = enabled;
308
309 enabled = newEnabled;
Jonathan Hartb35540a2015-11-17 09:30:56 -0800310 useBddp = newUseBddp;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700311 probeRate = newProbeRate;
312 staleLinkAge = newStaleLinkAge;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700313
314 if (!wasEnabled && enabled) {
315 enable();
316 } else if (wasEnabled && !enabled) {
317 disable();
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700318 } else {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700319 if (enabled) {
320 // update all discovery helper state
321 loadDevices();
322 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700323 }
324
Naoki Shiota399a0b32015-11-15 20:36:13 -0600325 log.info(FORMAT, enabled, useBddp, probeRate, staleLinkAge);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700326 }
327
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700328 /**
329 * Enables link discovery processing.
330 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700331 private void enable() {
alshabib7911a052014-10-16 17:49:37 -0700332 providerService = providerRegistry.register(this);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800333 masterService.addListener(roleListener);
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700334 deviceService.addListener(deviceListener);
335 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800336
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700337 loadDevices();
Thomas Vachuska05453c92015-09-09 14:40:49 -0700338
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700339 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/link", "discovery-%d"));
340 executor.scheduleAtFixedRate(new SyncDeviceInfoTask(),
341 DEVICE_SYNC_DELAY, DEVICE_SYNC_DELAY, SECONDS);
342 executor.scheduleAtFixedRate(new LinkPrunerTask(),
343 LINK_PRUNER_DELAY, LINK_PRUNER_DELAY, SECONDS);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700344
Thomas Vachuska05453c92015-09-09 14:40:49 -0700345 requestIntercepts();
346 }
347
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700348 /**
349 * Disables link discovery processing.
350 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700351 private void disable() {
352 withdrawIntercepts();
353
354 providerRegistry.unregister(this);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700355 masterService.removeListener(roleListener);
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700356 deviceService.removeListener(deviceListener);
357 packetService.removeProcessor(packetProcessor);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700358
Naoki Shiota399a0b32015-11-15 20:36:13 -0600359
Thomas Vachuska05453c92015-09-09 14:40:49 -0700360 if (executor != null) {
361 executor.shutdownNow();
362 }
363 discoverers.values().forEach(LinkDiscovery::stop);
364 discoverers.clear();
365
366 providerService = null;
367 }
368
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700369 /**
370 * Loads available devices and registers their ports to be probed.
371 */
372 private void loadDevices() {
Naoki Shiota399a0b32015-11-15 20:36:13 -0600373 if (!enabled) {
374 return;
375 }
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700376 deviceService.getAvailableDevices()
377 .forEach(d -> updateDevice(d)
378 .ifPresent(ld -> updatePorts(ld, d.id())));
379 }
380
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800381 private boolean isBlacklisted(DeviceId did) {
382 LinkDiscoveryFromDevice cfg = cfgRegistry.getConfig(did, LinkDiscoveryFromDevice.class);
383 if (cfg == null) {
384 return false;
385 }
386 return !cfg.enabled();
387 }
388
389 private boolean isBlacklisted(ConnectPoint cp) {
390 // if parent device is blacklisted, so is the port
391 if (isBlacklisted(cp.deviceId())) {
392 return true;
393 }
394 LinkDiscoveryFromPort cfg = cfgRegistry.getConfig(cp, LinkDiscoveryFromPort.class);
395 if (cfg == null) {
396 return false;
397 }
398 return !cfg.enabled();
399 }
400
401 private boolean isBlacklisted(Port port) {
402 return isBlacklisted(new ConnectPoint(port.element().id(), port.number()));
403 }
404
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800405 private boolean isFingerprinted(DeviceId did) {
406 FingerprintProbeFromDevice cfg = cfgRegistry.getConfig(did, FingerprintProbeFromDevice.class);
407 if (cfg == null) {
408 return false;
409 }
410 return cfg.enabled();
411 }
412
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700413 /**
414 * Updates discovery helper for specified device.
415 *
416 * Adds and starts a discovery helper for specified device if enabled,
417 * calls {@link #removeDevice(DeviceId)} otherwise.
418 *
419 * @param device device to add
420 * @return discovery helper if discovery is enabled for the device
421 */
422 private Optional<LinkDiscovery> updateDevice(Device device) {
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800423 if (device == null) {
424 return Optional.empty();
425 }
426 if (rules.isSuppressed(device) || isBlacklisted(device.id())) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700427 log.trace("LinkDiscovery from {} disabled by configuration", device.id());
428 removeDevice(device.id());
429 return Optional.empty();
430 }
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800431
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700432 LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
433 did -> new LinkDiscovery(device, context));
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800434 if (isFingerprinted(device.id())) {
435 ld.enableFingerprint();
436 } else {
437 ld.disableFingerprint();
438 }
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700439 if (ld.isStopped()) {
440 ld.start();
441 }
442 return Optional.of(ld);
443 }
444
445 /**
446 * Removes after stopping discovery helper for specified device.
447 * @param deviceId device to remove
448 */
449 private void removeDevice(final DeviceId deviceId) {
450 discoverers.computeIfPresent(deviceId, (did, ld) -> {
451 ld.stop();
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700452 return null;
453 });
454
455 }
456
457 /**
458 * Updates ports of the specified device to the specified discovery helper.
459 */
460 private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
461 deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
462 }
463
464 /**
465 * Updates discovery helper state of the specified port.
466 *
467 * Adds a port to the discovery helper if up and discovery is enabled,
468 * or calls {@link #removePort(Port)} otherwise.
469 */
470 private void updatePort(LinkDiscovery discoverer, Port port) {
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800471 if (port == null) {
472 return;
473 }
HIGUCHI Yutab853b3f2015-11-17 18:43:20 -0800474 if (port.number().isLogical()) {
475 // silently ignore logical ports
476 return;
477 }
Naoki Shiota399a0b32015-11-15 20:36:13 -0600478
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800479 if (rules.isSuppressed(port) || isBlacklisted(port)) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700480 log.trace("LinkDiscovery from {} disabled by configuration", port);
481 removePort(port);
482 return;
483 }
484
485 // check if enabled and turn off discovery?
486 if (!port.isEnabled()) {
487 removePort(port);
488 return;
489 }
490
HIGUCHI Yutab853b3f2015-11-17 18:43:20 -0800491 discoverer.addPort(port);
alshabib7911a052014-10-16 17:49:37 -0700492 }
493
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700494 /**
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700495 * Removes a port from the specified discovery helper.
496 * @param port the port
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700497 */
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700498 private void removePort(Port port) {
499 if (port.element() instanceof Device) {
500 Device d = (Device) port.element();
501 LinkDiscovery ld = discoverers.get(d.id());
502 if (ld != null) {
503 ld.removePort(port.number());
Jonathan Hart45066bc2015-07-28 11:18:34 -0700504 }
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700505 } else {
506 log.warn("Attempted to remove non-Device port", port);
Jonathan Hart45066bc2015-07-28 11:18:34 -0700507 }
508 }
509
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700510 /**
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700511 * Requests packet intercepts.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800512 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700513 private void requestIntercepts() {
514 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Thomas Vachuska347cc872015-09-23 10:25:29 -0700515 selector.matchEthType(TYPE_LLDP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700516 packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800517
Thomas Vachuska347cc872015-09-23 10:25:29 -0700518 selector.matchEthType(TYPE_BSN);
Jonathan Hartb35540a2015-11-17 09:30:56 -0800519 if (useBddp) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700520 packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
521 } else {
522 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800523 }
524 }
525
Thomas Vachuska27bee092015-06-23 19:03:10 -0700526 /**
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700527 * Withdraws packet intercepts.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700528 */
529 private void withdrawIntercepts() {
530 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Thomas Vachuska347cc872015-09-23 10:25:29 -0700531 selector.matchEthType(TYPE_LLDP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700532 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
Thomas Vachuska347cc872015-09-23 10:25:29 -0700533 selector.matchEthType(TYPE_BSN);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700534 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
535 }
536
Naoki Shiota399a0b32015-11-15 20:36:13 -0600537 protected SuppressionRules rules() {
538 return rules;
539 }
540
541 protected void updateRules(SuppressionRules newRules) {
542 if (!rules.equals(newRules)) {
543 rules = newRules;
544 loadDevices();
545 }
546 }
547
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700548 /**
549 * Processes device mastership role changes.
550 */
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800551 private class InternalRoleListener implements MastershipListener {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800552 @Override
553 public void event(MastershipEvent event) {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800554 if (MastershipEvent.Type.BACKUPS_CHANGED.equals(event.type())) {
555 // only need new master events
556 return;
557 }
558
559 DeviceId deviceId = event.subject();
560 Device device = deviceService.getDevice(deviceId);
561 if (device == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700562 log.debug("Device {} doesn't exist, or isn't there yet", deviceId);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800563 return;
564 }
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700565 if (clusterService.getLocalNode().id().equals(event.roleInfo().master())) {
566 updateDevice(device).ifPresent(ld -> updatePorts(ld, device.id()));
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800567 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800568 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800569 }
alshabib7911a052014-10-16 17:49:37 -0700570
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700571 /**
572 * Processes device events.
573 */
574 private class InternalDeviceListener implements DeviceListener {
alshabib7911a052014-10-16 17:49:37 -0700575 @Override
576 public void event(DeviceEvent event) {
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800577 if (event.type() == Type.PORT_STATS_UPDATED) {
578 return;
579 }
alshabib7911a052014-10-16 17:49:37 -0700580 Device device = event.subject();
alshabibacd91832014-10-17 14:38:41 -0700581 Port port = event.port();
alshabibdfc7afb2014-10-21 20:13:27 -0700582 if (device == null) {
583 log.error("Device is null.");
584 return;
585 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700586 log.trace("{} {} {}", event.type(), event.subject(), event);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700587 final DeviceId deviceId = device.id();
alshabib7911a052014-10-16 17:49:37 -0700588 switch (event.type()) {
589 case DEVICE_ADDED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700590 case DEVICE_UPDATED:
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700591 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
alshabib7911a052014-10-16 17:49:37 -0700592 break;
593 case PORT_ADDED:
594 case PORT_UPDATED:
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700595 if (port.isEnabled()) {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700596 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
alshabib7911a052014-10-16 17:49:37 -0700597 } else {
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700598 log.debug("Port down {}", port);
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700599 removePort(port);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600600 providerService.linksVanished(new ConnectPoint(port.element().id(),
601 port.number()));
alshabib7911a052014-10-16 17:49:37 -0700602 }
603 break;
604 case PORT_REMOVED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700605 log.debug("Port removed {}", port);
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700606 removePort(port);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600607 providerService.linksVanished(new ConnectPoint(port.element().id(),
608 port.number()));
alshabib7911a052014-10-16 17:49:37 -0700609 break;
610 case DEVICE_REMOVED:
611 case DEVICE_SUSPENDED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700612 log.debug("Device removed {}", deviceId);
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700613 removeDevice(deviceId);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600614 providerService.linksVanished(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700615 break;
616 case DEVICE_AVAILABILITY_CHANGED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700617 if (deviceService.isAvailable(deviceId)) {
618 log.debug("Device up {}", deviceId);
alshabibe3af2652015-12-01 23:05:34 -0800619 updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
alshabib7911a052014-10-16 17:49:37 -0700620 } else {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700621 log.debug("Device down {}", deviceId);
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700622 removeDevice(deviceId);
Naoki Shiota399a0b32015-11-15 20:36:13 -0600623 providerService.linksVanished(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700624 }
625 break;
Jonathan Hart9de692c2015-04-23 11:45:47 -0700626 case PORT_STATS_UPDATED:
627 break;
alshabib7911a052014-10-16 17:49:37 -0700628 default:
629 log.debug("Unknown event {}", event);
630 }
631 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700632 }
alshabib7911a052014-10-16 17:49:37 -0700633
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700634 /**
635 * Processes incoming packets.
636 */
637 private class InternalPacketProcessor implements PacketProcessor {
alshabib7911a052014-10-16 17:49:37 -0700638 @Override
639 public void process(PacketContext context) {
Thomas Vachuska347cc872015-09-23 10:25:29 -0700640 if (context == null || context.isHandled()) {
alshabib4a179dc2014-10-17 17:17:01 -0700641 return;
642 }
Thomas Vachuska347cc872015-09-23 10:25:29 -0700643
644 Ethernet eth = context.inPacket().parsed();
645 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
646 return;
647 }
648
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700649 LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId());
alshabib7911a052014-10-16 17:49:37 -0700650 if (ld == null) {
651 return;
652 }
653
Jonathan Hartb35540a2015-11-17 09:30:56 -0800654 if (ld.handleLldp(context)) {
alshabib7911a052014-10-16 17:49:37 -0700655 context.block();
656 }
657 }
658 }
659
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700660 /**
661 * Auxiliary task to keep device ports up to date.
662 */
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800663 private final class SyncDeviceInfoTask implements Runnable {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800664 @Override
665 public void run() {
666 if (Thread.currentThread().isInterrupted()) {
667 log.info("Interrupted, quitting");
668 return;
669 }
670 // check what deviceService sees, to see if we are missing anything
671 try {
HIGUCHI Yuta9a9edf82015-10-21 11:23:20 -0700672 loadDevices();
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800673 } catch (Exception e) {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700674 // Catch all exceptions to avoid task being suppressed
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800675 log.error("Exception thrown during synchronization process", e);
676 }
677 }
678 }
679
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700680 /**
681 * Auxiliary task for pruning stale links.
682 */
683 private class LinkPrunerTask implements Runnable {
684 @Override
685 public void run() {
686 if (Thread.currentThread().isInterrupted()) {
687 log.info("Interrupted, quitting");
688 return;
689 }
690
691 try {
692 // TODO: There is still a slight possibility of mastership
693 // change occurring right with link going stale. This will
694 // result in the stale link not being pruned.
695 Maps.filterEntries(linkTimes, e -> {
696 if (!masterService.isLocalMaster(e.getKey().dst().deviceId())) {
697 return true;
698 }
699 if (isStale(e.getValue())) {
700 providerService.linkVanished(new DefaultLinkDescription(e.getKey().src(),
701 e.getKey().dst(),
702 DIRECT));
703 return true;
704 }
705 return false;
706 }).clear();
707
708 } catch (Exception e) {
709 // Catch all exceptions to avoid task being suppressed
710 log.error("Exception thrown during link pruning process", e);
711 }
712 }
713
714 private boolean isStale(long lastSeen) {
715 return lastSeen < System.currentTimeMillis() - staleLinkAge;
716 }
717 }
718
719 /**
720 * Provides processing context for the device link discovery helpers.
721 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700722 private class InternalDiscoveryContext implements DiscoveryContext {
723 @Override
724 public MastershipService mastershipService() {
725 return masterService;
726 }
727
728 @Override
729 public LinkProviderService providerService() {
730 return providerService;
731 }
732
733 @Override
734 public PacketService packetService() {
735 return packetService;
736 }
737
738 @Override
739 public long probeRate() {
740 return probeRate;
741 }
742
743 @Override
Jonathan Hartb35540a2015-11-17 09:30:56 -0800744 public boolean useBddp() {
745 return useBddp;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700746 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700747
748 @Override
749 public void touchLink(LinkKey key) {
750 linkTimes.put(key, System.currentTimeMillis());
751 }
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800752
753 @Override
754 public String fingerprint() {
Ayaka Koshibe3ddb7b22015-12-10 17:32:59 -0800755 ClusterMetadata mdata = clusterMetadataService.getClusterMetadata();
756 return mdata == null ? NO_NAME : mdata.getName();
757 }
758
759 @Override
760 public DeviceService deviceService() {
761 return deviceService;
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800762 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700763 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700764
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800765 static final EnumSet<NetworkConfigEvent.Type> CONFIG_CHANGED
766 = EnumSet.of(NetworkConfigEvent.Type.CONFIG_ADDED,
767 NetworkConfigEvent.Type.CONFIG_UPDATED,
768 NetworkConfigEvent.Type.CONFIG_REMOVED);
769
Naoki Shiota399a0b32015-11-15 20:36:13 -0600770 private class InternalConfigListener implements NetworkConfigListener {
771
772 private synchronized void reconfigureSuppressionRules(SuppressionConfig cfg) {
773 if (cfg == null) {
774 log.error("Suppression Config is null.");
775 return;
776 }
777
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800778 SuppressionRules newRules = new SuppressionRules(cfg.deviceTypes(),
Naoki Shiota399a0b32015-11-15 20:36:13 -0600779 cfg.annotation());
780
781 updateRules(newRules);
782 }
783
784 @Override
785 public void event(NetworkConfigEvent event) {
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800786 if (event.configClass() == LinkDiscoveryFromDevice.class &&
787 CONFIG_CHANGED.contains(event.type())) {
788
789 if (event.subject() instanceof DeviceId) {
790 final DeviceId did = (DeviceId) event.subject();
791 Device device = deviceService.getDevice(did);
792 updateDevice(device).ifPresent(ld -> updatePorts(ld, did));
793 }
794
795 } else if (event.configClass() == LinkDiscoveryFromPort.class &&
796 CONFIG_CHANGED.contains(event.type())) {
797
798 if (event.subject() instanceof ConnectPoint) {
799 ConnectPoint cp = (ConnectPoint) event.subject();
800 if (cp.elementId() instanceof DeviceId) {
801 final DeviceId did = (DeviceId) cp.elementId();
802 Device device = deviceService.getDevice(did);
803 Port port = deviceService.getPort(did, cp.port());
804 updateDevice(device).ifPresent(ld -> updatePort(ld, port));
805 }
806 }
807
Ayaka Koshibe12c8c082015-12-08 12:48:46 -0800808 } else if (event.configClass() == FingerprintProbeFromDevice.class &&
809 CONFIG_CHANGED.contains(event.type())) {
810
811 if (event.subject() instanceof DeviceId) {
812 final DeviceId did = (DeviceId) event.subject();
813 Device device = deviceService.getDevice(did);
814 updateDevice(device);
815 }
816
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800817 } else if (event.configClass().equals(SuppressionConfig.class) &&
Naoki Shiota399a0b32015-11-15 20:36:13 -0600818 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
819 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) {
820 SuppressionConfig cfg = cfgRegistry.getConfig(appId, SuppressionConfig.class);
821 reconfigureSuppressionRules(cfg);
822 log.trace("Network config reconfigured");
823 }
824 }
825 }
alshabib7911a052014-10-16 17:49:37 -0700826}