blob: 0fe9676c2150c87da2bbd517b2b6c005f9a6fff1 [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
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080021import org.apache.felix.scr.annotations.Modified;
22import org.apache.felix.scr.annotations.Property;
alshabib7911a052014-10-16 17:49:37 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connorabafb502014-12-02 22:26:20 -080025import org.onosproject.mastership.MastershipEvent;
26import org.onosproject.mastership.MastershipListener;
27import org.onosproject.mastership.MastershipService;
28import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.Device;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.Port;
32import org.onosproject.net.device.DeviceEvent;
33import org.onosproject.net.device.DeviceListener;
34import org.onosproject.net.device.DeviceService;
35import org.onosproject.net.link.LinkProvider;
36import org.onosproject.net.link.LinkProviderRegistry;
37import org.onosproject.net.link.LinkProviderService;
38import org.onosproject.net.packet.PacketContext;
39import org.onosproject.net.packet.PacketProcessor;
40import org.onosproject.net.packet.PacketService;
41import org.onosproject.net.provider.AbstractProvider;
42import org.onosproject.net.provider.ProviderId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080043import org.osgi.service.component.ComponentContext;
alshabib7911a052014-10-16 17:49:37 -070044import org.slf4j.Logger;
45
Yuta HIGUCHI41289382014-12-19 17:47:12 -080046import com.google.common.base.Strings;
47import com.google.common.collect.ImmutableMap;
48import com.google.common.collect.ImmutableSet;
49
50import java.io.IOException;
51import java.util.Dictionary;
52import java.util.EnumSet;
alshabib7911a052014-10-16 17:49:37 -070053import java.util.Map;
54import java.util.concurrent.ConcurrentHashMap;
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -080055import java.util.concurrent.ScheduledExecutorService;
56import java.util.concurrent.TimeUnit;
alshabib7911a052014-10-16 17:49:37 -070057
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -080058import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
59import static org.onlab.util.Tools.namedThreads;
alshabib7911a052014-10-16 17:49:37 -070060import static org.slf4j.LoggerFactory.getLogger;
61
62
63/**
64 * Provider which uses an OpenFlow controller to detect network
65 * infrastructure links.
66 */
67@Component(immediate = true)
68public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
69
Yuta HIGUCHI41289382014-12-19 17:47:12 -080070
71 private static final String PROP_USE_BDDP = "useBDDP";
72
73 private static final String PROP_LLDP_SUPPRESSION = "lldpSuppression";
74
75 private static final String DEFAULT_LLDP_SUPPRESSION_CONFIG = "../config/lldp_suppression.json";
76
alshabib7911a052014-10-16 17:49:37 -070077 private final Logger log = getLogger(getClass());
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected LinkProviderRegistry providerRegistry;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected DeviceService deviceService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected PacketService packetSevice;
87
alshabib875d6262014-10-17 16:19:40 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected MastershipService masterService;
90
alshabib7911a052014-10-16 17:49:37 -070091 private LinkProviderService providerService;
92
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -080093 private ScheduledExecutorService executor;
94
Yuta HIGUCHI41289382014-12-19 17:47:12 -080095 @Property(name = PROP_USE_BDDP, boolValue = true,
96 label = "use BDDP for link discovery")
97 private boolean useBDDP = true;
alshabib7911a052014-10-16 17:49:37 -070098
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -080099 private static final long INIT_DELAY = 5;
100 private static final long DELAY = 5;
alshabib7911a052014-10-16 17:49:37 -0700101
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800102 @Property(name = PROP_LLDP_SUPPRESSION, value = DEFAULT_LLDP_SUPPRESSION_CONFIG,
103 label = "Path to LLDP suppression configuration file")
104 private String filePath = DEFAULT_LLDP_SUPPRESSION_CONFIG;
105
106
alshabib7911a052014-10-16 17:49:37 -0700107 private final InternalLinkProvider listener = new InternalLinkProvider();
108
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800109 private final InternalRoleListener roleListener = new InternalRoleListener();
110
alshabib7911a052014-10-16 17:49:37 -0700111 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
112
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800113 private SuppressionRules rules;
114
alshabib7911a052014-10-16 17:49:37 -0700115 /**
116 * Creates an OpenFlow link provider.
117 */
118 public LLDPLinkProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800119 super(new ProviderId("lldp", "org.onosproject.provider.lldp"));
alshabib7911a052014-10-16 17:49:37 -0700120 }
121
122 @Activate
123 public void activate() {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800124 loadSuppressionRules();
125
alshabib7911a052014-10-16 17:49:37 -0700126 providerService = providerRegistry.register(this);
127 deviceService.addListener(listener);
128 packetSevice.addProcessor(listener, 0);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800129 masterService.addListener(roleListener);
130
alshabibdfc7afb2014-10-21 20:13:27 -0700131 LinkDiscovery ld;
alshabib5dc5a342014-12-03 14:11:16 -0800132 for (Device device : deviceService.getAvailableDevices()) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800133 if (rules.isSuppressed(device)) {
134 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
135 continue;
136 }
alshabibdfc7afb2014-10-21 20:13:27 -0700137 ld = new LinkDiscovery(device, packetSevice, masterService,
138 providerService, useBDDP);
139 discoverers.put(device.id(), ld);
140 for (Port p : deviceService.getPorts(device.id())) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800141 if (rules.isSuppressed(p)) {
142 log.debug("LinkDiscovery from {}@{} disabled by configuration",
143 p.number(), device.id());
144 continue;
145 }
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700146 if (!p.number().isLogical()) {
147 ld.addPort(p);
148 }
alshabibdfc7afb2014-10-21 20:13:27 -0700149 }
150 }
alshabib7911a052014-10-16 17:49:37 -0700151
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800152 executor = newSingleThreadScheduledExecutor(namedThreads("device-sync-%d"));
153 executor.scheduleAtFixedRate(new SyncDeviceInfoTask(), INIT_DELAY,
154 DELAY, TimeUnit.SECONDS);
155
alshabib7911a052014-10-16 17:49:37 -0700156 log.info("Started");
157 }
158
159 @Deactivate
160 public void deactivate() {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800161 executor.shutdownNow();
alshabib7911a052014-10-16 17:49:37 -0700162 for (LinkDiscovery ld : discoverers.values()) {
163 ld.stop();
164 }
165 providerRegistry.unregister(this);
166 deviceService.removeListener(listener);
167 packetSevice.removeProcessor(listener);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800168 masterService.removeListener(roleListener);
alshabib7911a052014-10-16 17:49:37 -0700169 providerService = null;
170
171 log.info("Stopped");
172 }
173
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800174 @Modified
175 public void modified(ComponentContext context) {
176 if (context == null) {
177 return;
178 }
179 @SuppressWarnings("rawtypes")
180 Dictionary properties = context.getProperties();
181
182 String s = (String) properties.get(PROP_USE_BDDP);
183 if (Strings.isNullOrEmpty(s)) {
184 useBDDP = true;
185 } else {
186 useBDDP = Boolean.valueOf(s);
187 }
188 s = (String) properties.get(PROP_LLDP_SUPPRESSION);
189 if (Strings.isNullOrEmpty(s)) {
190 filePath = DEFAULT_LLDP_SUPPRESSION_CONFIG;
191 } else {
192 filePath = s;
193 }
194
195 loadSuppressionRules();
196 }
197
198 private void loadSuppressionRules() {
199 SuppressionRulesStore store = new SuppressionRulesStore(filePath);
200 try {
201 rules = store.read();
202 } catch (IOException e) {
203 log.info("Failed to load {}, using built-in rules", filePath);
204 // default rule to suppress ROADM to maintain compatibility
205 rules = new SuppressionRules(ImmutableSet.of(),
206 EnumSet.of(Device.Type.ROADM),
207 ImmutableMap.of());
208 }
209
210 // should refresh discoverers when we need dynamic reconfiguration
211 }
212
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800213 private class InternalRoleListener implements MastershipListener {
214
215 @Override
216 public void event(MastershipEvent event) {
217
218 if (MastershipEvent.Type.BACKUPS_CHANGED.equals(event.type())) {
219 // only need new master events
220 return;
221 }
222
223 DeviceId deviceId = event.subject();
224 Device device = deviceService.getDevice(deviceId);
225 if (device == null) {
226 log.warn("Device {} doesn't exist, or isn't there yet", deviceId);
227 return;
228 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800229 if (rules.isSuppressed(device)) {
230 return;
231 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800232 synchronized (discoverers) {
233 if (!discoverers.containsKey(deviceId)) {
alshabib4785eec2014-12-04 16:45:45 -0800234 // ideally, should never reach here
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800235 log.debug("Device mastership changed ({}) {}",
236 event.type(), deviceId);
237 discoverers.put(deviceId, new LinkDiscovery(device,
238 packetSevice, masterService, providerService,
239 useBDDP));
240 }
241 }
242 }
243
244 }
alshabib7911a052014-10-16 17:49:37 -0700245
246 private class InternalLinkProvider implements PacketProcessor, DeviceListener {
247
248 @Override
249 public void event(DeviceEvent event) {
250 LinkDiscovery ld = null;
251 Device device = event.subject();
alshabibacd91832014-10-17 14:38:41 -0700252 Port port = event.port();
alshabibdfc7afb2014-10-21 20:13:27 -0700253 if (device == null) {
254 log.error("Device is null.");
255 return;
256 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700257 log.trace("{} {} {}", event.type(), event.subject(), event);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700258 final DeviceId deviceId = device.id();
alshabib7911a052014-10-16 17:49:37 -0700259 switch (event.type()) {
260 case DEVICE_ADDED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700261 case DEVICE_UPDATED:
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800262 synchronized (discoverers) {
263 ld = discoverers.get(deviceId);
264 if (ld == null) {
265 if (rules.isSuppressed(device)) {
266 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
267 return;
268 }
269 log.debug("Device added ({}) {}", event.type(),
270 deviceId);
271 discoverers.put(deviceId, new LinkDiscovery(device,
272 packetSevice, masterService, providerService,
273 useBDDP));
274 } else {
275 if (ld.isStopped()) {
276 log.debug("Device restarted ({}) {}", event.type(),
277 deviceId);
278 ld.start();
279 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800280 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700281 }
alshabib7911a052014-10-16 17:49:37 -0700282 break;
283 case PORT_ADDED:
284 case PORT_UPDATED:
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700285 if (port.isEnabled()) {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700286 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700287 if (ld == null) {
288 return;
289 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800290 if (rules.isSuppressed(port)) {
291 log.debug("LinkDiscovery from {}@{} disabled by configuration",
292 port.number(), device.id());
293 return;
294 }
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700295 if (!port.number().isLogical()) {
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700296 log.debug("Port added {}", port);
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700297 ld.addPort(port);
298 }
alshabib7911a052014-10-16 17:49:37 -0700299 } else {
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700300 log.debug("Port down {}", port);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700301 ConnectPoint point = new ConnectPoint(deviceId,
alshabibacd91832014-10-17 14:38:41 -0700302 port.number());
alshabib7911a052014-10-16 17:49:37 -0700303 providerService.linksVanished(point);
304 }
305 break;
306 case PORT_REMOVED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700307 log.debug("Port removed {}", port);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700308 ConnectPoint point = new ConnectPoint(deviceId,
alshabibacd91832014-10-17 14:38:41 -0700309 port.number());
alshabib7911a052014-10-16 17:49:37 -0700310 providerService.linksVanished(point);
alshabib4785eec2014-12-04 16:45:45 -0800311
alshabib7911a052014-10-16 17:49:37 -0700312 break;
313 case DEVICE_REMOVED:
314 case DEVICE_SUSPENDED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700315 log.debug("Device removed {}", deviceId);
316 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700317 if (ld == null) {
318 return;
319 }
320 ld.stop();
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700321 providerService.linksVanished(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700322 break;
323 case DEVICE_AVAILABILITY_CHANGED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700324 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700325 if (ld == null) {
326 return;
327 }
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700328 if (deviceService.isAvailable(deviceId)) {
329 log.debug("Device up {}", deviceId);
alshabib7911a052014-10-16 17:49:37 -0700330 ld.start();
331 } else {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700332 providerService.linksVanished(deviceId);
333 log.debug("Device down {}", deviceId);
alshabib7911a052014-10-16 17:49:37 -0700334 ld.stop();
335 }
336 break;
alshabib7911a052014-10-16 17:49:37 -0700337 default:
338 log.debug("Unknown event {}", event);
339 }
340 }
341
342 @Override
343 public void process(PacketContext context) {
alshabib4a179dc2014-10-17 17:17:01 -0700344 if (context == null) {
345 return;
346 }
alshabib7911a052014-10-16 17:49:37 -0700347 LinkDiscovery ld = discoverers.get(
348 context.inPacket().receivedFrom().deviceId());
349 if (ld == null) {
350 return;
351 }
352
353 if (ld.handleLLDP(context)) {
354 context.block();
355 }
356 }
357 }
358
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800359 private final class SyncDeviceInfoTask implements Runnable {
360
361 @Override
362 public void run() {
363 if (Thread.currentThread().isInterrupted()) {
364 log.info("Interrupted, quitting");
365 return;
366 }
367 // check what deviceService sees, to see if we are missing anything
368 try {
369 LinkDiscovery ld = null;
370 for (Device dev : deviceService.getDevices()) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800371 if (rules.isSuppressed(dev)) {
372 continue;
373 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800374 DeviceId did = dev.id();
375 synchronized (discoverers) {
376 if (!discoverers.containsKey(did)) {
377 ld = new LinkDiscovery(dev, packetSevice,
378 masterService, providerService, useBDDP);
379 discoverers.put(did, ld);
380 for (Port p : deviceService.getPorts(did)) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800381 if (rules.isSuppressed(p)) {
382 continue;
383 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800384 if (!p.number().isLogical()) {
385 ld.addPort(p);
386 }
387 }
388 }
389 }
390 }
391 } catch (Exception e) {
392 // catch all Exception to avoid Scheduled task being suppressed.
393 log.error("Exception thrown during synchronization process", e);
394 }
395 }
396 }
397
alshabib7911a052014-10-16 17:49:37 -0700398}