blob: 0169e02cbf99c9029b3280f231e254dd435f0d8e [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;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080025import org.onlab.packet.Ethernet;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.mastership.MastershipEvent;
29import org.onosproject.mastership.MastershipListener;
30import org.onosproject.mastership.MastershipService;
31import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Port;
35import org.onosproject.net.device.DeviceEvent;
36import org.onosproject.net.device.DeviceListener;
37import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080038import org.onosproject.net.flow.DefaultFlowRule;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowRule;
42import org.onosproject.net.flow.FlowRuleService;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.link.LinkProvider;
46import org.onosproject.net.link.LinkProviderRegistry;
47import org.onosproject.net.link.LinkProviderService;
48import org.onosproject.net.packet.PacketContext;
49import org.onosproject.net.packet.PacketProcessor;
50import org.onosproject.net.packet.PacketService;
51import org.onosproject.net.provider.AbstractProvider;
52import org.onosproject.net.provider.ProviderId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -080053import org.osgi.service.component.ComponentContext;
alshabib7911a052014-10-16 17:49:37 -070054import org.slf4j.Logger;
55
Yuta HIGUCHI41289382014-12-19 17:47:12 -080056import com.google.common.base.Strings;
57import com.google.common.collect.ImmutableMap;
58import com.google.common.collect.ImmutableSet;
59
60import java.io.IOException;
61import java.util.Dictionary;
62import java.util.EnumSet;
alshabib7911a052014-10-16 17:49:37 -070063import java.util.Map;
64import java.util.concurrent.ConcurrentHashMap;
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -080065import java.util.concurrent.ScheduledExecutorService;
66import java.util.concurrent.TimeUnit;
alshabib7911a052014-10-16 17:49:37 -070067
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -080068import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
69import static org.onlab.util.Tools.namedThreads;
alshabib7911a052014-10-16 17:49:37 -070070import static org.slf4j.LoggerFactory.getLogger;
71
72
73/**
74 * Provider which uses an OpenFlow controller to detect network
75 * infrastructure links.
76 */
77@Component(immediate = true)
78public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
79
Yuta HIGUCHI41289382014-12-19 17:47:12 -080080 private static final String PROP_USE_BDDP = "useBDDP";
81
82 private static final String PROP_LLDP_SUPPRESSION = "lldpSuppression";
83
84 private static final String DEFAULT_LLDP_SUPPRESSION_CONFIG = "../config/lldp_suppression.json";
85
alshabib7911a052014-10-16 17:49:37 -070086 private final Logger log = getLogger(getClass());
87
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080088 private static final int FLOW_RULE_PRIORITY = 40000;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected CoreService coreService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected FlowRuleService flowRuleService;
95
alshabib7911a052014-10-16 17:49:37 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected LinkProviderRegistry providerRegistry;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DeviceService deviceService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected PacketService packetSevice;
104
alshabib875d6262014-10-17 16:19:40 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected MastershipService masterService;
107
alshabib7911a052014-10-16 17:49:37 -0700108 private LinkProviderService providerService;
109
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800110 private ScheduledExecutorService executor;
111
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800112 @Property(name = PROP_USE_BDDP, boolValue = true,
113 label = "use BDDP for link discovery")
114 private boolean useBDDP = true;
alshabib7911a052014-10-16 17:49:37 -0700115
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800116 private static final long INIT_DELAY = 5;
117 private static final long DELAY = 5;
alshabib7911a052014-10-16 17:49:37 -0700118
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800119 @Property(name = PROP_LLDP_SUPPRESSION, value = DEFAULT_LLDP_SUPPRESSION_CONFIG,
120 label = "Path to LLDP suppression configuration file")
121 private String filePath = DEFAULT_LLDP_SUPPRESSION_CONFIG;
122
123
alshabib7911a052014-10-16 17:49:37 -0700124 private final InternalLinkProvider listener = new InternalLinkProvider();
125
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800126 private final InternalRoleListener roleListener = new InternalRoleListener();
127
alshabib7911a052014-10-16 17:49:37 -0700128 protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
129
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800130 private SuppressionRules rules;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800131 private ApplicationId appId;
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800132
alshabib7911a052014-10-16 17:49:37 -0700133 /**
134 * Creates an OpenFlow link provider.
135 */
136 public LLDPLinkProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800137 super(new ProviderId("lldp", "org.onosproject.provider.lldp"));
alshabib7911a052014-10-16 17:49:37 -0700138 }
139
140 @Activate
141 public void activate() {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800142 appId =
143 coreService.registerApplication("org.onosproject.provider.lldp");
144
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800145 loadSuppressionRules();
146
alshabib7911a052014-10-16 17:49:37 -0700147 providerService = providerRegistry.register(this);
148 deviceService.addListener(listener);
149 packetSevice.addProcessor(listener, 0);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800150 masterService.addListener(roleListener);
151
alshabibdfc7afb2014-10-21 20:13:27 -0700152 LinkDiscovery ld;
alshabib5dc5a342014-12-03 14:11:16 -0800153 for (Device device : deviceService.getAvailableDevices()) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800154 if (rules.isSuppressed(device)) {
155 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
156 continue;
157 }
alshabibdfc7afb2014-10-21 20:13:27 -0700158 ld = new LinkDiscovery(device, packetSevice, masterService,
159 providerService, useBDDP);
160 discoverers.put(device.id(), ld);
161 for (Port p : deviceService.getPorts(device.id())) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800162 if (rules.isSuppressed(p)) {
163 log.debug("LinkDiscovery from {}@{} disabled by configuration",
164 p.number(), device.id());
165 continue;
166 }
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700167 if (!p.number().isLogical()) {
168 ld.addPort(p);
169 }
alshabibdfc7afb2014-10-21 20:13:27 -0700170 }
171 }
alshabib7911a052014-10-16 17:49:37 -0700172
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800173 executor = newSingleThreadScheduledExecutor(namedThreads("device-sync-%d"));
174 executor.scheduleAtFixedRate(new SyncDeviceInfoTask(), INIT_DELAY,
175 DELAY, TimeUnit.SECONDS);
176
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800177 pushRules();
178
alshabib7911a052014-10-16 17:49:37 -0700179 log.info("Started");
180 }
181
182 @Deactivate
183 public void deactivate() {
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800184 executor.shutdownNow();
alshabib7911a052014-10-16 17:49:37 -0700185 for (LinkDiscovery ld : discoverers.values()) {
186 ld.stop();
187 }
188 providerRegistry.unregister(this);
189 deviceService.removeListener(listener);
190 packetSevice.removeProcessor(listener);
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800191 masterService.removeListener(roleListener);
alshabib7911a052014-10-16 17:49:37 -0700192 providerService = null;
193
194 log.info("Stopped");
195 }
196
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800197 @Modified
198 public void modified(ComponentContext context) {
199 if (context == null) {
200 return;
201 }
202 @SuppressWarnings("rawtypes")
203 Dictionary properties = context.getProperties();
204
205 String s = (String) properties.get(PROP_USE_BDDP);
206 if (Strings.isNullOrEmpty(s)) {
207 useBDDP = true;
208 } else {
209 useBDDP = Boolean.valueOf(s);
210 }
211 s = (String) properties.get(PROP_LLDP_SUPPRESSION);
212 if (Strings.isNullOrEmpty(s)) {
213 filePath = DEFAULT_LLDP_SUPPRESSION_CONFIG;
214 } else {
215 filePath = s;
216 }
217
218 loadSuppressionRules();
219 }
220
221 private void loadSuppressionRules() {
222 SuppressionRulesStore store = new SuppressionRulesStore(filePath);
223 try {
224 rules = store.read();
225 } catch (IOException e) {
226 log.info("Failed to load {}, using built-in rules", filePath);
227 // default rule to suppress ROADM to maintain compatibility
228 rules = new SuppressionRules(ImmutableSet.of(),
229 EnumSet.of(Device.Type.ROADM),
230 ImmutableMap.of());
231 }
232
233 // should refresh discoverers when we need dynamic reconfiguration
234 }
235
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800236 /**
237 * Pushes flow rules to all devices.
238 */
239 private void pushRules() {
240 for (Device device : deviceService.getDevices()) {
241 pushRules(device);
242 }
243 }
244
245 /**
246 * Pushes flow rules to the device to receive control packets that need
247 * to be processed.
248 *
249 * @param device the device to push the rules to
250 */
251 private synchronized void pushRules(Device device) {
252 TrafficSelector.Builder sbuilder;
253 TrafficTreatment.Builder tbuilder;
254
255 // Get all LLDP packets
256 sbuilder = DefaultTrafficSelector.builder();
257 tbuilder = DefaultTrafficTreatment.builder();
258 sbuilder.matchEthType(Ethernet.TYPE_LLDP);
259 tbuilder.punt();
260 FlowRule flowLldp =
261 new DefaultFlowRule(device.id(),
262 sbuilder.build(), tbuilder.build(),
263 FLOW_RULE_PRIORITY, appId, 0, true);
264
265 // Get all BDDP packets
266 sbuilder = DefaultTrafficSelector.builder();
267 tbuilder = DefaultTrafficTreatment.builder();
268 sbuilder.matchEthType(Ethernet.TYPE_BSN);
269 tbuilder.punt();
270 FlowRule flowBddp =
271 new DefaultFlowRule(device.id(),
272 sbuilder.build(), tbuilder.build(),
273 FLOW_RULE_PRIORITY, appId, 0, true);
274
275 flowRuleService.applyFlowRules(flowLldp, flowBddp);
276 }
277
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800278 private class InternalRoleListener implements MastershipListener {
279
280 @Override
281 public void event(MastershipEvent event) {
282
283 if (MastershipEvent.Type.BACKUPS_CHANGED.equals(event.type())) {
284 // only need new master events
285 return;
286 }
287
288 DeviceId deviceId = event.subject();
289 Device device = deviceService.getDevice(deviceId);
290 if (device == null) {
291 log.warn("Device {} doesn't exist, or isn't there yet", deviceId);
292 return;
293 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800294 if (rules.isSuppressed(device)) {
295 return;
296 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800297 synchronized (discoverers) {
298 if (!discoverers.containsKey(deviceId)) {
alshabib4785eec2014-12-04 16:45:45 -0800299 // ideally, should never reach here
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800300 log.debug("Device mastership changed ({}) {}",
301 event.type(), deviceId);
302 discoverers.put(deviceId, new LinkDiscovery(device,
303 packetSevice, masterService, providerService,
304 useBDDP));
305 }
306 }
307 }
308
309 }
alshabib7911a052014-10-16 17:49:37 -0700310
311 private class InternalLinkProvider implements PacketProcessor, DeviceListener {
312
313 @Override
314 public void event(DeviceEvent event) {
315 LinkDiscovery ld = null;
316 Device device = event.subject();
alshabibacd91832014-10-17 14:38:41 -0700317 Port port = event.port();
alshabibdfc7afb2014-10-21 20:13:27 -0700318 if (device == null) {
319 log.error("Device is null.");
320 return;
321 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700322 log.trace("{} {} {}", event.type(), event.subject(), event);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700323 final DeviceId deviceId = device.id();
alshabib7911a052014-10-16 17:49:37 -0700324 switch (event.type()) {
325 case DEVICE_ADDED:
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800326 pushRules(device);
327 // FALLTHROUGH
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700328 case DEVICE_UPDATED:
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800329 synchronized (discoverers) {
330 ld = discoverers.get(deviceId);
331 if (ld == null) {
332 if (rules.isSuppressed(device)) {
333 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
334 return;
335 }
336 log.debug("Device added ({}) {}", event.type(),
337 deviceId);
338 discoverers.put(deviceId, new LinkDiscovery(device,
339 packetSevice, masterService, providerService,
340 useBDDP));
341 } else {
342 if (ld.isStopped()) {
343 log.debug("Device restarted ({}) {}", event.type(),
344 deviceId);
345 ld.start();
346 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800347 }
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700348 }
alshabib7911a052014-10-16 17:49:37 -0700349 break;
350 case PORT_ADDED:
351 case PORT_UPDATED:
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700352 if (port.isEnabled()) {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700353 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700354 if (ld == null) {
355 return;
356 }
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800357 if (rules.isSuppressed(port)) {
358 log.debug("LinkDiscovery from {}@{} disabled by configuration",
359 port.number(), device.id());
360 return;
361 }
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700362 if (!port.number().isLogical()) {
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700363 log.debug("Port added {}", port);
Yuta HIGUCHI00b476f2014-10-25 21:33:07 -0700364 ld.addPort(port);
365 }
alshabib7911a052014-10-16 17:49:37 -0700366 } else {
Yuta HIGUCHIf6725882014-10-29 15:25:51 -0700367 log.debug("Port down {}", port);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700368 ConnectPoint point = new ConnectPoint(deviceId,
alshabibacd91832014-10-17 14:38:41 -0700369 port.number());
alshabib7911a052014-10-16 17:49:37 -0700370 providerService.linksVanished(point);
371 }
372 break;
373 case PORT_REMOVED:
Yuta HIGUCHIeb24e9d2014-10-26 19:34:20 -0700374 log.debug("Port removed {}", port);
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700375 ConnectPoint point = new ConnectPoint(deviceId,
alshabibacd91832014-10-17 14:38:41 -0700376 port.number());
alshabib7911a052014-10-16 17:49:37 -0700377 providerService.linksVanished(point);
alshabib4785eec2014-12-04 16:45:45 -0800378
alshabib7911a052014-10-16 17:49:37 -0700379 break;
380 case DEVICE_REMOVED:
381 case DEVICE_SUSPENDED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700382 log.debug("Device removed {}", deviceId);
383 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700384 if (ld == null) {
385 return;
386 }
387 ld.stop();
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700388 providerService.linksVanished(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700389 break;
390 case DEVICE_AVAILABILITY_CHANGED:
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700391 ld = discoverers.get(deviceId);
alshabib7911a052014-10-16 17:49:37 -0700392 if (ld == null) {
393 return;
394 }
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700395 if (deviceService.isAvailable(deviceId)) {
396 log.debug("Device up {}", deviceId);
alshabib7911a052014-10-16 17:49:37 -0700397 ld.start();
398 } else {
Yuta HIGUCHId19f6702014-10-31 15:23:25 -0700399 providerService.linksVanished(deviceId);
400 log.debug("Device down {}", deviceId);
alshabib7911a052014-10-16 17:49:37 -0700401 ld.stop();
402 }
403 break;
alshabib7911a052014-10-16 17:49:37 -0700404 default:
405 log.debug("Unknown event {}", event);
406 }
407 }
408
409 @Override
410 public void process(PacketContext context) {
alshabib4a179dc2014-10-17 17:17:01 -0700411 if (context == null) {
412 return;
413 }
alshabib7911a052014-10-16 17:49:37 -0700414 LinkDiscovery ld = discoverers.get(
415 context.inPacket().receivedFrom().deviceId());
416 if (ld == null) {
417 return;
418 }
419
420 if (ld.handleLLDP(context)) {
421 context.block();
422 }
423 }
424 }
425
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800426 private final class SyncDeviceInfoTask implements Runnable {
427
428 @Override
429 public void run() {
430 if (Thread.currentThread().isInterrupted()) {
431 log.info("Interrupted, quitting");
432 return;
433 }
434 // check what deviceService sees, to see if we are missing anything
435 try {
436 LinkDiscovery ld = null;
437 for (Device dev : deviceService.getDevices()) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800438 if (rules.isSuppressed(dev)) {
439 continue;
440 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800441 DeviceId did = dev.id();
442 synchronized (discoverers) {
443 if (!discoverers.containsKey(did)) {
444 ld = new LinkDiscovery(dev, packetSevice,
445 masterService, providerService, useBDDP);
446 discoverers.put(did, ld);
447 for (Port p : deviceService.getPorts(did)) {
Yuta HIGUCHI41289382014-12-19 17:47:12 -0800448 if (rules.isSuppressed(p)) {
449 continue;
450 }
Ayaka Koshibeccfa94c2014-11-20 11:15:52 -0800451 if (!p.number().isLogical()) {
452 ld.addPort(p);
453 }
454 }
455 }
456 }
457 }
458 } catch (Exception e) {
459 // catch all Exception to avoid Scheduled task being suppressed.
460 log.error("Exception thrown during synchronization process", e);
461 }
462 }
463 }
464
alshabib7911a052014-10-16 17:49:37 -0700465}