blob: 97c4d2f176f77e76c8828e9ba5f3aa77d2163d04 [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
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080018import com.google.common.collect.ImmutableMap;
19import com.google.common.collect.ImmutableSet;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070020import 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 Vachuska6519e6f2015-03-11 02:29:31 -070029import org.onosproject.cfg.ComponentConfigService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080030import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.mastership.MastershipEvent;
33import org.onosproject.mastership.MastershipListener;
34import org.onosproject.mastership.MastershipService;
35import org.onosproject.net.ConnectPoint;
36import org.onosproject.net.Device;
37import org.onosproject.net.DeviceId;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070038import org.onosproject.net.LinkKey;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.Port;
40import org.onosproject.net.device.DeviceEvent;
41import org.onosproject.net.device.DeviceListener;
42import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080043import org.onosproject.net.flow.DefaultTrafficSelector;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080044import org.onosproject.net.flow.TrafficSelector;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070045import org.onosproject.net.link.DefaultLinkDescription;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.link.LinkProvider;
47import org.onosproject.net.link.LinkProviderRegistry;
48import org.onosproject.net.link.LinkProviderService;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070049import org.onosproject.net.link.LinkService;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080051import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.packet.PacketProcessor;
53import org.onosproject.net.packet.PacketService;
54import org.onosproject.net.provider.AbstractProvider;
55import org.onosproject.net.provider.ProviderId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080056import org.osgi.service.component.ComponentContext;
alshabib7911a052014-10-16 17:49:37 -070057import org.slf4j.Logger;
58
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080059import java.io.IOException;
60import java.util.Dictionary;
61import java.util.EnumSet;
62import java.util.Map;
Thomas Vachuska05453c92015-09-09 14:40:49 -070063import java.util.Properties;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080064import java.util.concurrent.ConcurrentHashMap;
65import java.util.concurrent.ScheduledExecutorService;
66
Thomas Vachuska05453c92015-09-09 14:40:49 -070067import static com.google.common.base.Strings.isNullOrEmpty;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080068import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
69import static java.util.concurrent.TimeUnit.SECONDS;
Thomas Vachuska347cc872015-09-23 10:25:29 -070070import static org.onlab.packet.Ethernet.TYPE_BSN;
71import static org.onlab.packet.Ethernet.TYPE_LLDP;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070072import static org.onlab.util.Tools.get;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080073import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuskae4ebac92015-09-10 11:39:05 -070074import static org.onosproject.net.Link.Type.DIRECT;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080075import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080076
alshabib7911a052014-10-16 17:49:37 -070077/**
Thomas Vachuska05453c92015-09-09 14:40:49 -070078 * Provider which uses LLDP and BDDP packets to detect network infrastructure links.
alshabib7911a052014-10-16 17:49:37 -070079 */
80@Component(immediate = true)
81public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
82
Thomas Vachuskafc52fec2015-05-18 19:13:56 -070083 private static final String PROVIDER_NAME = "org.onosproject.provider.lldp";
84
Thomas Vachuska05453c92015-09-09 14:40:49 -070085 private static final String FORMAT =
86 "Settings: enabled={}, useBDDP={}, probeRate={}, " +
87 "staleLinkAge={}, lldpSuppression={}";
Yuta HIGUCHI41289382014-12-19 17:47:12 -080088
alshabib7911a052014-10-16 17:49:37 -070089 private final Logger log = getLogger(getClass());
90
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected CoreService coreService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib7911a052014-10-16 17:49:37 -070095 protected LinkProviderRegistry providerRegistry;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected DeviceService deviceService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700101 protected LinkService linkService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Marc De Leenheer8b3e80b2015-03-06 14:27:03 -0800104 protected PacketService packetService;
alshabib7911a052014-10-16 17:49:37 -0700105
alshabib875d6262014-10-17 16:19:40 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected MastershipService masterService;
108
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected ComponentConfigService cfgService;
111
alshabib7911a052014-10-16 17:49:37 -0700112 private LinkProviderService providerService;
113
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800114 private ScheduledExecutorService executor;
115
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700116 // TODO: Add sanity checking for the configurable params based on the delays
117 private static final long DEVICE_SYNC_DELAY = 5;
118 private static final long LINK_PRUNER_DELAY = 3;
Thomas Vachuska05453c92015-09-09 14:40:49 -0700119
120 private static final String PROP_ENABLED = "enabled";
121 @Property(name = PROP_ENABLED, boolValue = true,
122 label = "If false, link discovery is disabled")
123 private boolean enabled = false;
124
125 private static final String PROP_USE_BDDP = "useBDDP";
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700126 @Property(name = PROP_USE_BDDP, boolValue = true,
127 label = "Use BDDP for link discovery")
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800128 private boolean useBDDP = true;
alshabib7911a052014-10-16 17:49:37 -0700129
Thomas Vachuska05453c92015-09-09 14:40:49 -0700130 private static final String PROP_PROBE_RATE = "probeRate";
131 private static final int DEFAULT_PROBE_RATE = 3_000;
132 @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
133 label = "LLDP and BDDP probe rate specified in millis")
134 private int probeRate = DEFAULT_PROBE_RATE;
Saurav Dasc313c402015-02-27 10:09:47 -0800135
Thomas Vachuska05453c92015-09-09 14:40:49 -0700136 private static final String PROP_STALE_LINK_AGE = "staleLinkAge";
137 private static final int DEFAULT_STALE_LINK_AGE = 10_000;
138 @Property(name = PROP_STALE_LINK_AGE, intValue = DEFAULT_STALE_LINK_AGE,
139 label = "Number of millis beyond which links will be considered stale")
140 private int staleLinkAge = DEFAULT_STALE_LINK_AGE;
alshabib7911a052014-10-16 17:49:37 -0700141
Thomas Vachuska05453c92015-09-09 14:40:49 -0700142 // FIXME: convert to use network config subsystem instead
143 private static final String PROP_LLDP_SUPPRESSION = "lldpSuppression";
144 private static final String DEFAULT_LLDP_SUPPRESSION_CONFIG = "../config/lldp_suppression.json";
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700145 @Property(name = PROP_LLDP_SUPPRESSION, value = DEFAULT_LLDP_SUPPRESSION_CONFIG,
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800146 label = "Path to LLDP suppression configuration file")
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700147 private String lldpSuppression = DEFAULT_LLDP_SUPPRESSION_CONFIG;
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800148
Thomas Vachuska05453c92015-09-09 14:40:49 -0700149 private final DiscoveryContext context = new InternalDiscoveryContext();
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800150 private final InternalRoleListener roleListener = new InternalRoleListener();
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700151 private final InternalDeviceListener deviceListener = new InternalDeviceListener();
152 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800153
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700154 // Device link discovery helpers.
alshabib7911a052014-10-16 17:49:37 -0700155 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
156
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700157 // Most recent time a tracked link was seen; links are tracked if their
158 // destination connection point is mastered by this controller instance.
159 private final Map<LinkKey, Long> linkTimes = Maps.newConcurrentMap();
160
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800161 private SuppressionRules rules;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800162 private ApplicationId appId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800163
alshabib7911a052014-10-16 17:49:37 -0700164 /**
165 * Creates an OpenFlow link provider.
166 */
167 public LLDPLinkProvider() {
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700168 super(new ProviderId("lldp", PROVIDER_NAME));
alshabib7911a052014-10-16 17:49:37 -0700169 }
170
171 @Activate
Saurav Dasc313c402015-02-27 10:09:47 -0800172 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700173 cfgService.registerProperties(getClass());
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700174 appId = coreService.registerApplication(PROVIDER_NAME);
Saurav Dasc313c402015-02-27 10:09:47 -0800175 modified(context);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700176 log.info("Started");
177 }
178
179 @Deactivate
180 public void deactivate() {
181 cfgService.unregisterProperties(getClass(), false);
182 disable();
183 log.info("Stopped");
184 }
185
186 @Modified
187 public void modified(ComponentContext context) {
188 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
189
190 boolean newEnabled, newUseBddp;
191 int newProbeRate, newStaleLinkAge;
192 String newLldpSuppression;
193 try {
194 String s = get(properties, PROP_ENABLED);
195 newEnabled = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
196
197 s = get(properties, PROP_USE_BDDP);
198 newUseBddp = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
199
200 s = get(properties, PROP_PROBE_RATE);
201 newProbeRate = isNullOrEmpty(s) ? probeRate : Integer.parseInt(s.trim());
202
203 s = get(properties, PROP_STALE_LINK_AGE);
204 newStaleLinkAge = isNullOrEmpty(s) ? staleLinkAge : Integer.parseInt(s.trim());
205
206 s = get(properties, PROP_LLDP_SUPPRESSION);
207 newLldpSuppression = isNullOrEmpty(s) ? DEFAULT_LLDP_SUPPRESSION_CONFIG : s;
208
209 } catch (NumberFormatException e) {
210 log.warn(e.getMessage());
211 newEnabled = enabled;
212 newUseBddp = useBDDP;
213 newProbeRate = probeRate;
214 newStaleLinkAge = staleLinkAge;
215 newLldpSuppression = lldpSuppression;
Saurav Dasc313c402015-02-27 10:09:47 -0800216 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800217
Thomas Vachuska05453c92015-09-09 14:40:49 -0700218 boolean wasEnabled = enabled;
219
220 enabled = newEnabled;
221 useBDDP = newUseBddp;
222 probeRate = newProbeRate;
223 staleLinkAge = newStaleLinkAge;
224 lldpSuppression = newLldpSuppression;
225
226 if (!wasEnabled && enabled) {
227 enable();
228 } else if (wasEnabled && !enabled) {
229 disable();
230 }
231
232 log.info(FORMAT, enabled, useBDDP, probeRate, staleLinkAge, lldpSuppression);
233 }
234
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700235 /**
236 * Enables link discovery processing.
237 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700238 private void enable() {
alshabib7911a052014-10-16 17:49:37 -0700239 providerService = providerRegistry.register(this);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800240 masterService.addListener(roleListener);
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700241 deviceService.addListener(deviceListener);
242 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800243
Jon Hall3edc08a2015-09-14 16:59:07 -0700244 loadSuppressionRules();
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700245 loadDevices();
Thomas Vachuska05453c92015-09-09 14:40:49 -0700246
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700247 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/link", "discovery-%d"));
248 executor.scheduleAtFixedRate(new SyncDeviceInfoTask(),
249 DEVICE_SYNC_DELAY, DEVICE_SYNC_DELAY, SECONDS);
250 executor.scheduleAtFixedRate(new LinkPrunerTask(),
251 LINK_PRUNER_DELAY, LINK_PRUNER_DELAY, SECONDS);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700252
Thomas Vachuska05453c92015-09-09 14:40:49 -0700253 requestIntercepts();
254 }
255
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700256 /**
257 * Disables link discovery processing.
258 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700259 private void disable() {
260 withdrawIntercepts();
261
262 providerRegistry.unregister(this);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700263 masterService.removeListener(roleListener);
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700264 deviceService.removeListener(deviceListener);
265 packetService.removeProcessor(packetProcessor);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700266
267 if (executor != null) {
268 executor.shutdownNow();
269 }
270 discoverers.values().forEach(LinkDiscovery::stop);
271 discoverers.clear();
272
273 providerService = null;
274 }
275
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700276 /**
277 * Loads available devices and registers their ports to be probed.
278 */
279 private void loadDevices() {
alshabib5dc5a342014-12-03 14:11:16 -0800280 for (Device device : deviceService.getAvailableDevices()) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800281 if (rules.isSuppressed(device)) {
282 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
283 continue;
284 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700285 LinkDiscovery ld = new LinkDiscovery(device, context);
alshabibdfc7afb2014-10-21 20:13:27 -0700286 discoverers.put(device.id(), ld);
Jonathan Hart45066bc2015-07-28 11:18:34 -0700287 addPorts(ld, device.id());
alshabibdfc7afb2014-10-21 20:13:27 -0700288 }
alshabib7911a052014-10-16 17:49:37 -0700289 }
290
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700291 /**
292 * Adds ports of the specified device to the specified discovery helper.
293 */
Jonathan Hart45066bc2015-07-28 11:18:34 -0700294 private void addPorts(LinkDiscovery discoverer, DeviceId deviceId) {
295 for (Port p : deviceService.getPorts(deviceId)) {
296 if (rules.isSuppressed(p)) {
297 continue;
298 }
299 if (!p.number().isLogical()) {
300 discoverer.addPort(p);
301 }
302 }
303 }
304
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700305
306 /**
307 * Loads LLDP suppression rules.
308 */
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800309 private void loadSuppressionRules() {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700310 // FIXME: convert to use network configuration
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700311 SuppressionRulesStore store = new SuppressionRulesStore(lldpSuppression);
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800312 try {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700313 log.info("Reading suppression rules from {}", lldpSuppression);
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800314 rules = store.read();
315 } catch (IOException e) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700316 log.info("Failed to load {}, using built-in rules", lldpSuppression);
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800317 // default rule to suppress ROADM to maintain compatibility
318 rules = new SuppressionRules(ImmutableSet.of(),
319 EnumSet.of(Device.Type.ROADM),
320 ImmutableMap.of());
321 }
322
323 // should refresh discoverers when we need dynamic reconfiguration
324 }
325
Charles M.C. Chane148de82015-05-06 12:38:21 +0800326 /**
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700327 * Requests packet intercepts.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800328 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700329 private void requestIntercepts() {
330 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Thomas Vachuska347cc872015-09-23 10:25:29 -0700331 selector.matchEthType(TYPE_LLDP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700332 packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800333
Thomas Vachuska347cc872015-09-23 10:25:29 -0700334 selector.matchEthType(TYPE_BSN);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800335 if (useBDDP) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700336 packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
337 } else {
338 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800339 }
340 }
341
Thomas Vachuska27bee092015-06-23 19:03:10 -0700342 /**
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700343 * Withdraws packet intercepts.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700344 */
345 private void withdrawIntercepts() {
346 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Thomas Vachuska347cc872015-09-23 10:25:29 -0700347 selector.matchEthType(TYPE_LLDP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700348 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
Thomas Vachuska347cc872015-09-23 10:25:29 -0700349 selector.matchEthType(TYPE_BSN);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700350 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
351 }
352
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700353 /**
354 * Processes device mastership role changes.
355 */
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800356 private class InternalRoleListener implements MastershipListener {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800357 @Override
358 public void event(MastershipEvent event) {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800359 if (MastershipEvent.Type.BACKUPS_CHANGED.equals(event.type())) {
360 // only need new master events
361 return;
362 }
363
364 DeviceId deviceId = event.subject();
365 Device device = deviceService.getDevice(deviceId);
366 if (device == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700367 log.debug("Device {} doesn't exist, or isn't there yet", deviceId);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800368 return;
369 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800370 if (rules.isSuppressed(device)) {
371 return;
372 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700373 discoverers.computeIfAbsent(deviceId, k -> new LinkDiscovery(device, context));
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800374 }
375
376 }
alshabib7911a052014-10-16 17:49:37 -0700377
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700378 /**
379 * Processes device events.
380 */
381 private class InternalDeviceListener implements DeviceListener {
alshabib7911a052014-10-16 17:49:37 -0700382 @Override
383 public void event(DeviceEvent event) {
Thomas Vachuska05453c92015-09-09 14:40:49 -0700384 LinkDiscovery ld;
alshabib7911a052014-10-16 17:49:37 -0700385 Device device = event.subject();
alshabibacd91832014-10-17 14:38:41 -0700386 Port port = event.port();
alshabibdfc7afb2014-10-21 20:13:27 -0700387 if (device == null) {
388 log.error("Device is null.");
389 return;
390 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700391 log.trace("{} {} {}", event.type(), event.subject(), event);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700392 final DeviceId deviceId = device.id();
alshabib7911a052014-10-16 17:49:37 -0700393 switch (event.type()) {
394 case DEVICE_ADDED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700395 case DEVICE_UPDATED:
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800396 synchronized (discoverers) {
397 ld = discoverers.get(deviceId);
398 if (ld == null) {
399 if (rules.isSuppressed(device)) {
400 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
401 return;
402 }
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700403 log.debug("Device added ({}) {}", event.type(), deviceId);
Thomas Vachuska05453c92015-09-09 14:40:49 -0700404 discoverers.put(deviceId, new LinkDiscovery(device, context));
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800405 } else {
406 if (ld.isStopped()) {
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700407 log.debug("Device restarted ({}) {}", event.type(), deviceId);
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800408 ld.start();
409 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800410 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700411 }
alshabib7911a052014-10-16 17:49:37 -0700412 break;
413 case PORT_ADDED:
414 case PORT_UPDATED:
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700415 if (port.isEnabled()) {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700416 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700417 if (ld == null) {
418 return;
419 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800420 if (rules.isSuppressed(port)) {
421 log.debug("LinkDiscovery from {}@{} disabled by configuration",
422 port.number(), device.id());
423 return;
424 }
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700425 if (!port.number().isLogical()) {
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700426 log.debug("Port added {}", port);
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700427 ld.addPort(port);
428 }
alshabib7911a052014-10-16 17:49:37 -0700429 } else {
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700430 log.debug("Port down {}", port);
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700431 ConnectPoint point = new ConnectPoint(deviceId, port.number());
alshabib7911a052014-10-16 17:49:37 -0700432 providerService.linksVanished(point);
433 }
434 break;
435 case PORT_REMOVED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700436 log.debug("Port removed {}", port);
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700437 ConnectPoint point = new ConnectPoint(deviceId, port.number());
alshabib7911a052014-10-16 17:49:37 -0700438 providerService.linksVanished(point);
alshabib4785eec2014-12-04 16:45:45 -0800439
alshabib7911a052014-10-16 17:49:37 -0700440 break;
441 case DEVICE_REMOVED:
442 case DEVICE_SUSPENDED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700443 log.debug("Device removed {}", deviceId);
444 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700445 if (ld == null) {
446 return;
447 }
448 ld.stop();
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700449 providerService.linksVanished(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700450 break;
451 case DEVICE_AVAILABILITY_CHANGED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700452 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700453 if (ld == null) {
454 return;
455 }
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700456 if (deviceService.isAvailable(deviceId)) {
457 log.debug("Device up {}", deviceId);
alshabib7911a052014-10-16 17:49:37 -0700458 ld.start();
459 } else {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700460 providerService.linksVanished(deviceId);
461 log.debug("Device down {}", deviceId);
alshabib7911a052014-10-16 17:49:37 -0700462 ld.stop();
463 }
464 break;
Jonathan Hart9de692c2015-04-23 11:45:47 -0700465 case PORT_STATS_UPDATED:
466 break;
alshabib7911a052014-10-16 17:49:37 -0700467 default:
468 log.debug("Unknown event {}", event);
469 }
470 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700471 }
alshabib7911a052014-10-16 17:49:37 -0700472
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700473 /**
474 * Processes incoming packets.
475 */
476 private class InternalPacketProcessor implements PacketProcessor {
alshabib7911a052014-10-16 17:49:37 -0700477 @Override
478 public void process(PacketContext context) {
Thomas Vachuska347cc872015-09-23 10:25:29 -0700479 if (context == null || context.isHandled()) {
alshabib4a179dc2014-10-17 17:17:01 -0700480 return;
481 }
Thomas Vachuska347cc872015-09-23 10:25:29 -0700482
483 Ethernet eth = context.inPacket().parsed();
484 if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
485 return;
486 }
487
Thomas Vachuska96f3ea72015-09-08 13:50:12 -0700488 LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId());
alshabib7911a052014-10-16 17:49:37 -0700489 if (ld == null) {
490 return;
491 }
492
493 if (ld.handleLLDP(context)) {
494 context.block();
495 }
496 }
497 }
498
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700499 /**
500 * Auxiliary task to keep device ports up to date.
501 */
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800502 private final class SyncDeviceInfoTask implements Runnable {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800503 @Override
504 public void run() {
505 if (Thread.currentThread().isInterrupted()) {
506 log.info("Interrupted, quitting");
507 return;
508 }
509 // check what deviceService sees, to see if we are missing anything
510 try {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800511 for (Device dev : deviceService.getDevices()) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800512 if (rules.isSuppressed(dev)) {
513 continue;
514 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800515 DeviceId did = dev.id();
516 synchronized (discoverers) {
Thomas Vachuska05453c92015-09-09 14:40:49 -0700517 LinkDiscovery ld = discoverers
518 .computeIfAbsent(did, k -> new LinkDiscovery(dev, context));
519 addPorts(ld, did);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800520 }
521 }
522 } catch (Exception e) {
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700523 // Catch all exceptions to avoid task being suppressed
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800524 log.error("Exception thrown during synchronization process", e);
525 }
526 }
527 }
528
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700529 /**
530 * Auxiliary task for pruning stale links.
531 */
532 private class LinkPrunerTask implements Runnable {
533 @Override
534 public void run() {
535 if (Thread.currentThread().isInterrupted()) {
536 log.info("Interrupted, quitting");
537 return;
538 }
539
540 try {
541 // TODO: There is still a slight possibility of mastership
542 // change occurring right with link going stale. This will
543 // result in the stale link not being pruned.
544 Maps.filterEntries(linkTimes, e -> {
545 if (!masterService.isLocalMaster(e.getKey().dst().deviceId())) {
546 return true;
547 }
548 if (isStale(e.getValue())) {
549 providerService.linkVanished(new DefaultLinkDescription(e.getKey().src(),
550 e.getKey().dst(),
551 DIRECT));
552 return true;
553 }
554 return false;
555 }).clear();
556
557 } catch (Exception e) {
558 // Catch all exceptions to avoid task being suppressed
559 log.error("Exception thrown during link pruning process", e);
560 }
561 }
562
563 private boolean isStale(long lastSeen) {
564 return lastSeen < System.currentTimeMillis() - staleLinkAge;
565 }
566 }
567
568 /**
569 * Provides processing context for the device link discovery helpers.
570 */
Thomas Vachuska05453c92015-09-09 14:40:49 -0700571 private class InternalDiscoveryContext implements DiscoveryContext {
572 @Override
573 public MastershipService mastershipService() {
574 return masterService;
575 }
576
577 @Override
578 public LinkProviderService providerService() {
579 return providerService;
580 }
581
582 @Override
583 public PacketService packetService() {
584 return packetService;
585 }
586
587 @Override
588 public long probeRate() {
589 return probeRate;
590 }
591
592 @Override
Thomas Vachuska05453c92015-09-09 14:40:49 -0700593 public boolean useBDDP() {
594 return useBDDP;
595 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700596
597 @Override
598 public void touchLink(LinkKey key) {
599 linkTimes.put(key, System.currentTimeMillis());
600 }
Thomas Vachuska05453c92015-09-09 14:40:49 -0700601 }
Thomas Vachuskae4ebac92015-09-10 11:39:05 -0700602
alshabib7911a052014-10-16 17:49:37 -0700603}