blob: 7f29cc5e7c613d7dc14e80dbb900bd5f9e7ea727 [file] [log] [blame]
Michele Santuari208b1672016-08-04 17:33:12 +02001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Michele Santuari208b1672016-08-04 17:33:12 +02003 *
4 * 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
7 *
8 * 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.
15 */
16
17package org.onosproject.provider.linkdiscovery.impl;
18
DongRyeol Cha3c377802019-11-14 15:40:22 +090019import com.google.common.collect.Sets;
20import org.onlab.util.Tools;
Michele Santuari208b1672016-08-04 17:33:12 +020021import org.onosproject.cfg.ComponentConfigService;
22import org.onosproject.core.ApplicationId;
23import org.onosproject.core.CoreService;
24import org.onosproject.mastership.MastershipService;
25import org.onosproject.net.AnnotationKeys;
26import org.onosproject.net.DefaultAnnotations;
27import org.onosproject.net.Device;
28import org.onosproject.net.DeviceId;
29import org.onosproject.net.Link;
30import org.onosproject.net.behaviour.LinkDiscovery;
31import org.onosproject.net.device.DeviceEvent;
32import org.onosproject.net.device.DeviceListener;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.link.DefaultLinkDescription;
35import org.onosproject.net.link.LinkDescription;
36import org.onosproject.net.link.LinkProvider;
37import org.onosproject.net.link.LinkProviderRegistry;
38import org.onosproject.net.link.LinkProviderService;
39import org.onosproject.net.link.LinkService;
40import org.onosproject.net.provider.AbstractProvider;
41import org.onosproject.net.provider.ProviderId;
42import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070043import org.osgi.service.component.annotations.Activate;
44import org.osgi.service.component.annotations.Component;
45import org.osgi.service.component.annotations.Deactivate;
46import org.osgi.service.component.annotations.Modified;
47import org.osgi.service.component.annotations.Reference;
48import org.osgi.service.component.annotations.ReferenceCardinality;
Michele Santuari208b1672016-08-04 17:33:12 +020049import org.slf4j.Logger;
50
51import java.util.Dictionary;
52import java.util.HashSet;
DongRyeol Cha3c377802019-11-14 15:40:22 +090053import java.util.Objects;
Michele Santuari208b1672016-08-04 17:33:12 +020054import java.util.Set;
DongRyeol Cha3c377802019-11-14 15:40:22 +090055import java.util.concurrent.CompletableFuture;
56import java.util.concurrent.ExecutionException;
57import java.util.concurrent.ExecutorService;
58import java.util.concurrent.Executors;
59import java.util.concurrent.ForkJoinPool;
Michele Santuari208b1672016-08-04 17:33:12 +020060import java.util.concurrent.ScheduledExecutorService;
61import java.util.concurrent.ScheduledFuture;
DongRyeol Cha3c377802019-11-14 15:40:22 +090062import java.util.concurrent.TimeoutException;
Michele Santuari208b1672016-08-04 17:33:12 +020063import java.util.stream.Collectors;
64
65import static com.google.common.base.Strings.isNullOrEmpty;
66import static java.util.concurrent.Executors.newScheduledThreadPool;
67import static java.util.concurrent.TimeUnit.SECONDS;
68import static org.onlab.util.Tools.get;
69import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070070import static org.onosproject.provider.linkdiscovery.impl.OsgiPropertyConstants.*;
Michele Santuari208b1672016-08-04 17:33:12 +020071import static org.slf4j.LoggerFactory.getLogger;
72
73/**
74 * Link provider capable of polling the environment using the device driver
75 * {@link LinkDiscovery} behaviour.
76 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070077@Component(immediate = true,
78 property = {
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070079 POLL_DELAY_SECONDS + ":Integer=" + POLL_DELAY_SECONDS_DEFAULT,
80 POLL_FREQUENCY_SECONDS + ":Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
DongRyeol Cha3c377802019-11-14 15:40:22 +090081 LINK_DISCOVERY_TIMEOUT_SECONDS + ":Integer=" + POLL_DISCOVERY_TIMEOUT_DEFAULT,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070082 })
Michele Santuari208b1672016-08-04 17:33:12 +020083public class LinkDiscoveryProvider extends AbstractProvider
84 implements LinkProvider {
85
86 protected static final String APP_NAME = "org.onosproject.linkdiscovery";
87 protected static final String SCHEME_NAME = "linkdiscovery";
88 private static final String LINK_PROVIDER_PACKAGE = "org.onosproject.provider.linkdiscovery";
89 private final Logger log = getLogger(getClass());
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070090
91 /** Initial delay (in seconds) for polling link discovery. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070092 protected static int linkPollDelaySeconds = POLL_DELAY_SECONDS_DEFAULT;
93
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070094 /** Frequency (in seconds) for polling link discovery. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070095 protected static int linkPollFrequencySeconds = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +020096
DongRyeol Cha3c377802019-11-14 15:40:22 +090097 /** Discovery timeout (in seconds) for polling arp discovery. */
98 protected static int linkDiscoveryTimeoutSeconds = POLL_DISCOVERY_TIMEOUT_DEFAULT;
99
100 private static final int POOL_SIZE = 10;
Michele Santuari208b1672016-08-04 17:33:12 +0200101
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +0200103 protected LinkProviderRegistry providerRegistry;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +0200105 protected CoreService coreService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +0200107 protected LinkService linkService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +0200109 protected MastershipService mastershipService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +0200111 protected DeviceService deviceService;
DongRyeol Cha3c377802019-11-14 15:40:22 +0900112 protected ExecutorService linkDiscoveryExecutor =
113 Executors.newFixedThreadPool(POOL_SIZE, groupedThreads("onos/linkdiscoveryprovider",
114 "link-collector-%d", log));
Michele Santuari208b1672016-08-04 17:33:12 +0200115 protected ScheduledExecutorService executor =
116 newScheduledThreadPool(2, groupedThreads("onos/netconf-link",
117 "discovery-%d"));
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +0200119 protected ComponentConfigService cfgService;
120
121 protected LinkProviderService providerService;
122 private InternalDeviceListener deviceListener = new InternalDeviceListener();
123 private ApplicationId appId;
124 private ScheduledFuture<?> scheduledTask;
DongRyeol Cha3c377802019-11-14 15:40:22 +0900125 private ForkJoinPool scheduledTaskPool = new ForkJoinPool(POOL_SIZE);
Michele Santuari208b1672016-08-04 17:33:12 +0200126
127 /**
128 * Creates a provider with the supplied identifier.
129 */
130 public LinkDiscoveryProvider() {
131 super(new ProviderId(SCHEME_NAME, LINK_PROVIDER_PACKAGE));
132 }
133
134 @Activate
135 public void activate(ComponentContext context) {
136 providerService = providerRegistry.register(this);
137 appId = coreService.registerApplication(APP_NAME);
138 deviceService.addListener(deviceListener);
139 cfgService.registerProperties(getClass());
140
141 if (context == null) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700142 linkPollFrequencySeconds = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200143 log.info("No component configuration");
144 } else {
145 Dictionary<?, ?> properties = context.getProperties();
146 linkPollFrequencySeconds =
147 getNewPollFrequency(properties, linkPollFrequencySeconds);
148 }
149 scheduledTask = schedulePolling();
150 log.info("Started");
151 }
152
153 @Deactivate
154 public void deactivate() {
155 cfgService.unregisterProperties(getClass(), false);
156 deviceService.removeListener(deviceListener);
157 providerRegistry.unregister(this);
158 providerService = null;
159 scheduledTask.cancel(true);
160 executor.shutdown();
161 log.info("Stopped");
162 }
163
164 @Modified
165 public void modified(ComponentContext context) {
166 if (context == null) {
167 log.info("No component configuration");
168 return;
169 } else {
170 Dictionary<?, ?> properties = context.getProperties();
171
172 int newPollFrequency = getNewPollFrequency(properties, linkPollFrequencySeconds);
173 int newPollDelay = getNewPollDealy(properties, linkPollDelaySeconds);
DongRyeol Cha3c377802019-11-14 15:40:22 +0900174 int newDiscoveryTimeout = getNewDiscoveryTimeout(properties, linkDiscoveryTimeoutSeconds);
Michele Santuari208b1672016-08-04 17:33:12 +0200175 if (newPollFrequency != linkPollFrequencySeconds ||
DongRyeol Cha3c377802019-11-14 15:40:22 +0900176 newPollDelay != linkPollDelaySeconds ||
177 newDiscoveryTimeout != linkDiscoveryTimeoutSeconds) {
Michele Santuari208b1672016-08-04 17:33:12 +0200178 linkPollFrequencySeconds = newPollFrequency;
179 linkPollDelaySeconds = newPollDelay;
DongRyeol Cha3c377802019-11-14 15:40:22 +0900180 linkDiscoveryTimeoutSeconds = newDiscoveryTimeout;
Michele Santuari208b1672016-08-04 17:33:12 +0200181 //stops the old scheduled task
182 scheduledTask.cancel(true);
183 //schedules new task at the new polling rate
184 scheduledTask = schedulePolling();
185 }
186 }
187 log.info("Modified");
188 }
189
190 private int getNewPollFrequency(Dictionary<?, ?> properties, int pollFrequency) {
191 int newPollFrequency;
192 try {
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700193 String s = get(properties, POLL_FREQUENCY_SECONDS);
Michele Santuari208b1672016-08-04 17:33:12 +0200194 newPollFrequency = isNullOrEmpty(s) ? pollFrequency : Integer.parseInt(s.trim());
195 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700196 newPollFrequency = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200197 }
198 return newPollFrequency;
199 }
200
201 private int getNewPollDealy(Dictionary<?, ?> properties, int pollDelay) {
202 int newPollFrequency;
203 try {
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700204 String s = get(properties, POLL_DELAY_SECONDS);
Michele Santuari208b1672016-08-04 17:33:12 +0200205 newPollFrequency = isNullOrEmpty(s) ? pollDelay : Integer.parseInt(s.trim());
206 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700207 newPollFrequency = POLL_DELAY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200208 }
209 return newPollFrequency;
210 }
211
DongRyeol Cha3c377802019-11-14 15:40:22 +0900212 private int getNewDiscoveryTimeout(Dictionary<?, ?> properties, int discoveryTimeout) {
213 int newDiscoveryTimeout;
214 try {
215 String s = get(properties, LINK_DISCOVERY_TIMEOUT_SECONDS);
216 newDiscoveryTimeout = isNullOrEmpty(s) ? discoveryTimeout : Integer.parseInt(s.trim());
217 } catch (NumberFormatException | ClassCastException e) {
218 newDiscoveryTimeout = POLL_DISCOVERY_TIMEOUT_DEFAULT;
219 log.error("Cannot update Discovery Timeout", e);
220 }
221 return newDiscoveryTimeout;
222 }
223
Michele Santuari208b1672016-08-04 17:33:12 +0200224 private ScheduledFuture schedulePolling() {
DongRyeol Cha3c377802019-11-14 15:40:22 +0900225 log.info("schedule: discoverLinksTasks with {} sec, {} sec",
226 linkPollDelaySeconds, linkPollFrequencySeconds);
Michele Santuari208b1672016-08-04 17:33:12 +0200227 return executor.scheduleAtFixedRate(this::discoverLinksTasks,
228 linkPollDelaySeconds,
229 linkPollFrequencySeconds,
230 SECONDS);
231 }
232
DongRyeol Cha3c377802019-11-14 15:40:22 +0900233 private void discoverLinks(Device device) {
234 DeviceId deviceId = device.id();
235 Set<LinkDescription> response = null;
236 try {
237 response = CompletableFuture.supplyAsync(() -> device.as(LinkDiscovery.class).getLinks(),
238 linkDiscoveryExecutor)
239 .exceptionally(e -> {
240 log.error("Exception is occurred during update the links. Device id {} {}", deviceId, e);
241 return null;
242 })
243 .get(linkDiscoveryTimeoutSeconds, SECONDS);
244 } catch (TimeoutException e) {
245 log.error("Timout is occurred during update the links. Device id {}, Timeout {}",
246 deviceId, linkDiscoveryTimeoutSeconds);
247 } catch (InterruptedException | ExecutionException e) {
248 log.warn("Exception is occurred during update the links. Device id {}, Timeout {}",
249 deviceId, linkDiscoveryTimeoutSeconds);
250 }
251 if (Objects.isNull(response)) {
252 return;
253 }
254 evaluateLinks(deviceId, response);
255 }
256
Michele Santuari208b1672016-08-04 17:33:12 +0200257 private void discoverLinksTasks() {
DongRyeol Cha3c377802019-11-14 15:40:22 +0900258 try {
259 scheduledTaskPool.submit(exceptionSafe(() -> {
260 Tools.stream(deviceService.getAvailableDevices()).parallel().forEach(device -> exceptionSafe(() -> {
261 if (isSupported(device)) {
262 discoverLinks(device);
263 }
264 }).run());
265 })).get();
266 } catch (Exception e) {
267 log.info("Unhandled exception {}", e.getMessage());
268 }
Michele Santuari208b1672016-08-04 17:33:12 +0200269 }
270
271 private void evaluateLinks(DeviceId deviceId, Set<LinkDescription> discoveredLinksDesc) {
Jose Miguel Guzmand4499a32016-10-31 09:21:08 -0300272 if (discoveredLinksDesc == null) {
273 return;
274 }
275
Michele Santuari208b1672016-08-04 17:33:12 +0200276 //The provider will get only existing links related to LinkDiscovery
DongRyeol Cha9547fc12018-07-24 15:26:16 +0900277 Set<Link> storedLinks = linkService.getDeviceIngressLinks(deviceId)
Michele Santuari208b1672016-08-04 17:33:12 +0200278 .stream()
279 .filter(link -> {
280 String value = link.annotations().value(AnnotationKeys.PROTOCOL);
DongRyeol Cha3c377802019-11-14 15:40:22 +0900281 return Objects.equals(value, SCHEME_NAME.toUpperCase());
Michele Santuari208b1672016-08-04 17:33:12 +0200282 })
283 .collect(Collectors.toSet());
284
285 //Convert Link to LinkDescription for comparison
286 Set<LinkDescription> storedLinkDescs = new HashSet<>();
287 storedLinks.forEach(link -> storedLinkDescs
288 .add(new DefaultLinkDescription(
289 link.src(), link.dst(), link.type(), link.isExpected(),
290 DefaultAnnotations.builder().putAll(link.annotations()).build())));
291 log.debug("Current stored links provider related {}", storedLinks);
292
293 //Add the correct annotation for comparison
294 Set<LinkDescription> discoveredLinkDescsAnn = new HashSet<>();
295
296 discoveredLinksDesc.forEach(linkDesc -> discoveredLinkDescsAnn
297 .add(new DefaultLinkDescription(
298 linkDesc.src(), linkDesc.dst(), linkDesc.type(), false,
299 DefaultAnnotations.builder().putAll(linkDesc.annotations())
300 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
301 .build())));
302
303 Set<LinkDescription> linkDescsToBeRemoved = new HashSet<>(storedLinkDescs);
304 linkDescsToBeRemoved.removeAll(discoveredLinkDescsAnn);
305 log.debug("Links to be removed {}", linkDescsToBeRemoved);
306 linkDescsToBeRemoved.forEach(linkDesc ->
307 providerService.linkVanished(linkDesc));
308
309 Set<LinkDescription> linksToBeAdded = new HashSet<>(discoveredLinkDescsAnn);
310 linksToBeAdded.removeAll(storedLinkDescs);
311 log.debug("Links to be added {}", linksToBeAdded);
312 linksToBeAdded.forEach(linkDesc -> providerService.linkDetected(linkDesc)
313 );
314 }
315
316 protected boolean isSupported(Device device) {
317 boolean supported = mastershipService.isLocalMaster(device.id())
318 && device.is(LinkDiscovery.class);
319 if (!supported) {
320 log.debug("Device {} does not support LinkDiscovery", device);
321 }
322 return supported;
323 }
324
325 /**
326 * Listener for core device events.
327 */
328 private class InternalDeviceListener implements DeviceListener {
329 @Override
330 public void event(DeviceEvent event) {
DongRyeol Cha3c377802019-11-14 15:40:22 +0900331 Device device = event.subject();
332 switch (event.type()) {
333 case DEVICE_ADDED:
334 executor.execute(() -> discoverLinks(device));
335 break;
336 case DEVICE_REMOVED:
337 evaluateLinks(device.id(), Sets.newHashSet());
338 break;
339 default:
340 log.debug("No implemented action for other DeviceEvents for the device {}", device.id());
341 break;
Michele Santuari208b1672016-08-04 17:33:12 +0200342 }
343 }
344
345 @Override
346 public boolean isRelevant(DeviceEvent event) {
347 return isSupported(event.subject());
348 }
349 }
DongRyeol Cha3c377802019-11-14 15:40:22 +0900350
351 private Runnable exceptionSafe(Runnable runnable) {
352 return () -> {
353 try {
354 runnable.run();
355 } catch (Exception e) {
356 log.error("Unhandled Exception", e);
357 }
358 };
359 }
Michele Santuari208b1672016-08-04 17:33:12 +0200360}