blob: 6aefaedc99fed63f5745265a07eabcead446fdde [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 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
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080018import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
19import static org.onlab.util.Tools.namedThreads;
20import static org.slf4j.LoggerFactory.getLogger;
21
22import java.io.IOException;
23import java.util.Dictionary;
24import java.util.EnumSet;
25import java.util.Map;
26import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.ScheduledExecutorService;
28import java.util.concurrent.TimeUnit;
29
alshabib7911a052014-10-16 17:49:37 -070030import org.apache.felix.scr.annotations.Activate;
31import org.apache.felix.scr.annotations.Component;
32import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080033import org.apache.felix.scr.annotations.Modified;
34import org.apache.felix.scr.annotations.Property;
alshabib7911a052014-10-16 17:49:37 -070035import org.apache.felix.scr.annotations.Reference;
36import org.apache.felix.scr.annotations.ReferenceCardinality;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080037import org.onlab.packet.Ethernet;
38import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.mastership.MastershipEvent;
41import org.onosproject.mastership.MastershipListener;
42import org.onosproject.mastership.MastershipService;
43import org.onosproject.net.ConnectPoint;
44import org.onosproject.net.Device;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.Port;
47import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080050import org.onosproject.net.flow.DefaultTrafficSelector;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080051import org.onosproject.net.flow.TrafficSelector;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.link.LinkProvider;
53import org.onosproject.net.link.LinkProviderRegistry;
54import org.onosproject.net.link.LinkProviderService;
55import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080056import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080057import org.onosproject.net.packet.PacketProcessor;
58import org.onosproject.net.packet.PacketService;
59import org.onosproject.net.provider.AbstractProvider;
60import org.onosproject.net.provider.ProviderId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080061import org.osgi.service.component.ComponentContext;
alshabib7911a052014-10-16 17:49:37 -070062import org.slf4j.Logger;
63
Yuta HIGUCHI41289382014-12-19 17:47:12 -080064import com.google.common.base.Strings;
65import com.google.common.collect.ImmutableMap;
66import com.google.common.collect.ImmutableSet;
67
alshabib7911a052014-10-16 17:49:37 -070068
69/**
70 * Provider which uses an OpenFlow controller to detect network
71 * infrastructure links.
72 */
73@Component(immediate = true)
74public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
75
Yuta HIGUCHI41289382014-12-19 17:47:12 -080076 private static final String PROP_USE_BDDP = "useBDDP";
77
78 private static final String PROP_LLDP_SUPPRESSION = "lldpSuppression";
79
80 private static final String DEFAULT_LLDP_SUPPRESSION_CONFIG = "../config/lldp_suppression.json";
81
alshabib7911a052014-10-16 17:49:37 -070082 private final Logger log = getLogger(getClass());
83
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080084 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected CoreService coreService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib7911a052014-10-16 17:49:37 -070088 protected LinkProviderRegistry providerRegistry;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected DeviceService deviceService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected PacketService packetSevice;
95
alshabib875d6262014-10-17 16:19:40 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected MastershipService masterService;
98
alshabib7911a052014-10-16 17:49:37 -070099 private LinkProviderService providerService;
100
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800101 private ScheduledExecutorService executor;
102
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800103 @Property(name = PROP_USE_BDDP, boolValue = true,
104 label = "use BDDP for link discovery")
105 private boolean useBDDP = true;
alshabib7911a052014-10-16 17:49:37 -0700106
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800107 private static final long INIT_DELAY = 5;
108 private static final long DELAY = 5;
alshabib7911a052014-10-16 17:49:37 -0700109
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800110 @Property(name = PROP_LLDP_SUPPRESSION, value = DEFAULT_LLDP_SUPPRESSION_CONFIG,
111 label = "Path to LLDP suppression configuration file")
112 private String filePath = DEFAULT_LLDP_SUPPRESSION_CONFIG;
113
114
alshabib7911a052014-10-16 17:49:37 -0700115 private final InternalLinkProvider listener = new InternalLinkProvider();
116
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800117 private final InternalRoleListener roleListener = new InternalRoleListener();
118
alshabib7911a052014-10-16 17:49:37 -0700119 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
120
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800121 private SuppressionRules rules;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800122 private ApplicationId appId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800123
alshabib7911a052014-10-16 17:49:37 -0700124 /**
125 * Creates an OpenFlow link provider.
126 */
127 public LLDPLinkProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800128 super(new ProviderId("lldp", "org.onosproject.provider.lldp"));
alshabib7911a052014-10-16 17:49:37 -0700129 }
130
131 @Activate
132 public void activate() {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800133 appId =
134 coreService.registerApplication("org.onosproject.provider.lldp");
135
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800136 loadSuppressionRules();
137
alshabib7911a052014-10-16 17:49:37 -0700138 providerService = providerRegistry.register(this);
139 deviceService.addListener(listener);
140 packetSevice.addProcessor(listener, 0);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800141 masterService.addListener(roleListener);
142
alshabibdfc7afb2014-10-21 20:13:27 -0700143 LinkDiscovery ld;
alshabib5dc5a342014-12-03 14:11:16 -0800144 for (Device device : deviceService.getAvailableDevices()) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800145 if (rules.isSuppressed(device)) {
146 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
147 continue;
148 }
alshabibdfc7afb2014-10-21 20:13:27 -0700149 ld = new LinkDiscovery(device, packetSevice, masterService,
150 providerService, useBDDP);
151 discoverers.put(device.id(), ld);
152 for (Port p : deviceService.getPorts(device.id())) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800153 if (rules.isSuppressed(p)) {
154 log.debug("LinkDiscovery from {}@{} disabled by configuration",
155 p.number(), device.id());
156 continue;
157 }
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700158 if (!p.number().isLogical()) {
159 ld.addPort(p);
160 }
alshabibdfc7afb2014-10-21 20:13:27 -0700161 }
162 }
alshabib7911a052014-10-16 17:49:37 -0700163
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800164 executor = newSingleThreadScheduledExecutor(namedThreads("device-sync-%d"));
165 executor.scheduleAtFixedRate(new SyncDeviceInfoTask(), INIT_DELAY,
166 DELAY, TimeUnit.SECONDS);
167
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800168 requestPackets();
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800169
alshabib7911a052014-10-16 17:49:37 -0700170 log.info("Started");
171 }
172
173 @Deactivate
174 public void deactivate() {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800175 executor.shutdownNow();
alshabib7911a052014-10-16 17:49:37 -0700176 for (LinkDiscovery ld : discoverers.values()) {
177 ld.stop();
178 }
179 providerRegistry.unregister(this);
180 deviceService.removeListener(listener);
181 packetSevice.removeProcessor(listener);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800182 masterService.removeListener(roleListener);
alshabib7911a052014-10-16 17:49:37 -0700183 providerService = null;
184
185 log.info("Stopped");
186 }
187
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800188 @Modified
189 public void modified(ComponentContext context) {
190 if (context == null) {
191 return;
192 }
193 @SuppressWarnings("rawtypes")
194 Dictionary properties = context.getProperties();
195
196 String s = (String) properties.get(PROP_USE_BDDP);
197 if (Strings.isNullOrEmpty(s)) {
198 useBDDP = true;
199 } else {
200 useBDDP = Boolean.valueOf(s);
201 }
202 s = (String) properties.get(PROP_LLDP_SUPPRESSION);
203 if (Strings.isNullOrEmpty(s)) {
204 filePath = DEFAULT_LLDP_SUPPRESSION_CONFIG;
205 } else {
206 filePath = s;
207 }
208
209 loadSuppressionRules();
210 }
211
212 private void loadSuppressionRules() {
213 SuppressionRulesStore store = new SuppressionRulesStore(filePath);
214 try {
215 rules = store.read();
216 } catch (IOException e) {
217 log.info("Failed to load {}, using built-in rules", filePath);
218 // default rule to suppress ROADM to maintain compatibility
219 rules = new SuppressionRules(ImmutableSet.of(),
220 EnumSet.of(Device.Type.ROADM),
221 ImmutableMap.of());
222 }
223
224 // should refresh discoverers when we need dynamic reconfiguration
225 }
226
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800227 private void requestPackets() {
228 TrafficSelector.Builder lldpSelector = DefaultTrafficSelector.builder();
229 lldpSelector.matchEthType(Ethernet.TYPE_LLDP);
230 packetSevice.requestPackets(lldpSelector.build(),
231 PacketPriority.CONTROL, appId);
232
233 if (useBDDP) {
234 TrafficSelector.Builder bddpSelector = DefaultTrafficSelector.builder();
235 bddpSelector.matchEthType(Ethernet.TYPE_BSN);
236 packetSevice.requestPackets(bddpSelector.build(),
237 PacketPriority.CONTROL, appId);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800238 }
239 }
240
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800241 private class InternalRoleListener implements MastershipListener {
242
243 @Override
244 public void event(MastershipEvent event) {
245
246 if (MastershipEvent.Type.BACKUPS_CHANGED.equals(event.type())) {
247 // only need new master events
248 return;
249 }
250
251 DeviceId deviceId = event.subject();
252 Device device = deviceService.getDevice(deviceId);
253 if (device == null) {
254 log.warn("Device {} doesn't exist, or isn't there yet", deviceId);
255 return;
256 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800257 if (rules.isSuppressed(device)) {
258 return;
259 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800260 synchronized (discoverers) {
261 if (!discoverers.containsKey(deviceId)) {
alshabib4785eec2014-12-04 16:45:45 -0800262 // ideally, should never reach here
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800263 log.debug("Device mastership changed ({}) {}",
264 event.type(), deviceId);
265 discoverers.put(deviceId, new LinkDiscovery(device,
266 packetSevice, masterService, providerService,
267 useBDDP));
268 }
269 }
270 }
271
272 }
alshabib7911a052014-10-16 17:49:37 -0700273
274 private class InternalLinkProvider implements PacketProcessor, DeviceListener {
275
276 @Override
277 public void event(DeviceEvent event) {
278 LinkDiscovery ld = null;
279 Device device = event.subject();
alshabibacd91832014-10-17 14:38:41 -0700280 Port port = event.port();
alshabibdfc7afb2014-10-21 20:13:27 -0700281 if (device == null) {
282 log.error("Device is null.");
283 return;
284 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700285 log.trace("{} {} {}", event.type(), event.subject(), event);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700286 final DeviceId deviceId = device.id();
alshabib7911a052014-10-16 17:49:37 -0700287 switch (event.type()) {
288 case DEVICE_ADDED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700289 case DEVICE_UPDATED:
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800290 synchronized (discoverers) {
291 ld = discoverers.get(deviceId);
292 if (ld == null) {
293 if (rules.isSuppressed(device)) {
294 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
295 return;
296 }
297 log.debug("Device added ({}) {}", event.type(),
298 deviceId);
299 discoverers.put(deviceId, new LinkDiscovery(device,
300 packetSevice, masterService, providerService,
301 useBDDP));
302 } else {
303 if (ld.isStopped()) {
304 log.debug("Device restarted ({}) {}", event.type(),
305 deviceId);
306 ld.start();
307 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800308 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700309 }
alshabib7911a052014-10-16 17:49:37 -0700310 break;
311 case PORT_ADDED:
312 case PORT_UPDATED:
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700313 if (port.isEnabled()) {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700314 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700315 if (ld == null) {
316 return;
317 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800318 if (rules.isSuppressed(port)) {
319 log.debug("LinkDiscovery from {}@{} disabled by configuration",
320 port.number(), device.id());
321 return;
322 }
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700323 if (!port.number().isLogical()) {
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700324 log.debug("Port added {}", port);
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700325 ld.addPort(port);
326 }
alshabib7911a052014-10-16 17:49:37 -0700327 } else {
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700328 log.debug("Port down {}", port);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700329 ConnectPoint point = new ConnectPoint(deviceId,
alshabibacd91832014-10-17 14:38:41 -0700330 port.number());
alshabib7911a052014-10-16 17:49:37 -0700331 providerService.linksVanished(point);
332 }
333 break;
334 case PORT_REMOVED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700335 log.debug("Port removed {}", port);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700336 ConnectPoint point = new ConnectPoint(deviceId,
alshabibacd91832014-10-17 14:38:41 -0700337 port.number());
alshabib7911a052014-10-16 17:49:37 -0700338 providerService.linksVanished(point);
alshabib4785eec2014-12-04 16:45:45 -0800339
alshabib7911a052014-10-16 17:49:37 -0700340 break;
341 case DEVICE_REMOVED:
342 case DEVICE_SUSPENDED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700343 log.debug("Device removed {}", deviceId);
344 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700345 if (ld == null) {
346 return;
347 }
348 ld.stop();
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700349 providerService.linksVanished(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700350 break;
351 case DEVICE_AVAILABILITY_CHANGED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700352 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700353 if (ld == null) {
354 return;
355 }
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700356 if (deviceService.isAvailable(deviceId)) {
357 log.debug("Device up {}", deviceId);
alshabib7911a052014-10-16 17:49:37 -0700358 ld.start();
359 } else {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700360 providerService.linksVanished(deviceId);
361 log.debug("Device down {}", deviceId);
alshabib7911a052014-10-16 17:49:37 -0700362 ld.stop();
363 }
364 break;
alshabib7911a052014-10-16 17:49:37 -0700365 default:
366 log.debug("Unknown event {}", event);
367 }
368 }
369
370 @Override
371 public void process(PacketContext context) {
alshabib4a179dc2014-10-17 17:17:01 -0700372 if (context == null) {
373 return;
374 }
alshabib7911a052014-10-16 17:49:37 -0700375 LinkDiscovery ld = discoverers.get(
376 context.inPacket().receivedFrom().deviceId());
377 if (ld == null) {
378 return;
379 }
380
381 if (ld.handleLLDP(context)) {
382 context.block();
383 }
384 }
385 }
386
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800387 private final class SyncDeviceInfoTask implements Runnable {
388
389 @Override
390 public void run() {
391 if (Thread.currentThread().isInterrupted()) {
392 log.info("Interrupted, quitting");
393 return;
394 }
395 // check what deviceService sees, to see if we are missing anything
396 try {
397 LinkDiscovery ld = null;
398 for (Device dev : deviceService.getDevices()) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800399 if (rules.isSuppressed(dev)) {
400 continue;
401 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800402 DeviceId did = dev.id();
403 synchronized (discoverers) {
404 if (!discoverers.containsKey(did)) {
405 ld = new LinkDiscovery(dev, packetSevice,
406 masterService, providerService, useBDDP);
407 discoverers.put(did, ld);
408 for (Port p : deviceService.getPorts(did)) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800409 if (rules.isSuppressed(p)) {
410 continue;
411 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800412 if (!p.number().isLogical()) {
413 ld.addPort(p);
414 }
415 }
416 }
417 }
418 }
419 } catch (Exception e) {
420 // catch all Exception to avoid Scheduled task being suppressed.
421 log.error("Exception thrown during synchronization process", e);
422 }
423 }
424 }
425
alshabib7911a052014-10-16 17:49:37 -0700426}