blob: dba7addb0cf738e80851c4bd8bea26d24ba080b6 [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
Michele Santuari208b1672016-08-04 17:33:12 +020019import org.onosproject.cfg.ComponentConfigService;
20import org.onosproject.core.ApplicationId;
21import org.onosproject.core.CoreService;
22import org.onosproject.mastership.MastershipService;
23import org.onosproject.net.AnnotationKeys;
24import org.onosproject.net.DefaultAnnotations;
25import org.onosproject.net.Device;
26import org.onosproject.net.DeviceId;
27import org.onosproject.net.Link;
28import org.onosproject.net.behaviour.LinkDiscovery;
29import org.onosproject.net.device.DeviceEvent;
30import org.onosproject.net.device.DeviceListener;
31import org.onosproject.net.device.DeviceService;
32import org.onosproject.net.link.DefaultLinkDescription;
33import org.onosproject.net.link.LinkDescription;
34import org.onosproject.net.link.LinkProvider;
35import org.onosproject.net.link.LinkProviderRegistry;
36import org.onosproject.net.link.LinkProviderService;
37import org.onosproject.net.link.LinkService;
38import org.onosproject.net.provider.AbstractProvider;
39import org.onosproject.net.provider.ProviderId;
40import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070041import org.osgi.service.component.annotations.Activate;
42import org.osgi.service.component.annotations.Component;
43import org.osgi.service.component.annotations.Deactivate;
44import org.osgi.service.component.annotations.Modified;
45import org.osgi.service.component.annotations.Reference;
46import org.osgi.service.component.annotations.ReferenceCardinality;
Michele Santuari208b1672016-08-04 17:33:12 +020047import org.slf4j.Logger;
48
49import java.util.Dictionary;
50import java.util.HashSet;
51import java.util.Set;
52import java.util.concurrent.ScheduledExecutorService;
53import java.util.concurrent.ScheduledFuture;
54import java.util.stream.Collectors;
55
56import static com.google.common.base.Strings.isNullOrEmpty;
57import static java.util.concurrent.Executors.newScheduledThreadPool;
58import static java.util.concurrent.TimeUnit.SECONDS;
59import static org.onlab.util.Tools.get;
60import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070061import static org.onosproject.provider.linkdiscovery.impl.OsgiPropertyConstants.*;
Michele Santuari208b1672016-08-04 17:33:12 +020062import static org.slf4j.LoggerFactory.getLogger;
63
64/**
65 * Link provider capable of polling the environment using the device driver
66 * {@link LinkDiscovery} behaviour.
67 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070068@Component(immediate = true,
69 property = {
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070070 POLL_DELAY_SECONDS + ":Integer=" + POLL_DELAY_SECONDS_DEFAULT,
71 POLL_FREQUENCY_SECONDS + ":Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070072 })
Michele Santuari208b1672016-08-04 17:33:12 +020073public class LinkDiscoveryProvider extends AbstractProvider
74 implements LinkProvider {
75
76 protected static final String APP_NAME = "org.onosproject.linkdiscovery";
77 protected static final String SCHEME_NAME = "linkdiscovery";
78 private static final String LINK_PROVIDER_PACKAGE = "org.onosproject.provider.linkdiscovery";
79 private final Logger log = getLogger(getClass());
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070080
81 /** Initial delay (in seconds) for polling link discovery. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070082 protected static int linkPollDelaySeconds = POLL_DELAY_SECONDS_DEFAULT;
83
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070084 /** Frequency (in seconds) for polling link discovery. */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070085 protected static int linkPollFrequencySeconds = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +020086
87
Ray Milkeyd84f89b2018-08-17 14:54:17 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020089 protected LinkProviderRegistry providerRegistry;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020091 protected CoreService coreService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020093 protected LinkService linkService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070094 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020095 protected MastershipService mastershipService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020097 protected DeviceService deviceService;
98 protected ScheduledExecutorService executor =
99 newScheduledThreadPool(2, groupedThreads("onos/netconf-link",
100 "discovery-%d"));
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +0200102 protected ComponentConfigService cfgService;
103
104 protected LinkProviderService providerService;
105 private InternalDeviceListener deviceListener = new InternalDeviceListener();
106 private ApplicationId appId;
107 private ScheduledFuture<?> scheduledTask;
108
109 /**
110 * Creates a provider with the supplied identifier.
111 */
112 public LinkDiscoveryProvider() {
113 super(new ProviderId(SCHEME_NAME, LINK_PROVIDER_PACKAGE));
114 }
115
116 @Activate
117 public void activate(ComponentContext context) {
118 providerService = providerRegistry.register(this);
119 appId = coreService.registerApplication(APP_NAME);
120 deviceService.addListener(deviceListener);
121 cfgService.registerProperties(getClass());
122
123 if (context == null) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700124 linkPollFrequencySeconds = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200125 log.info("No component configuration");
126 } else {
127 Dictionary<?, ?> properties = context.getProperties();
128 linkPollFrequencySeconds =
129 getNewPollFrequency(properties, linkPollFrequencySeconds);
130 }
131 scheduledTask = schedulePolling();
132 log.info("Started");
133 }
134
135 @Deactivate
136 public void deactivate() {
137 cfgService.unregisterProperties(getClass(), false);
138 deviceService.removeListener(deviceListener);
139 providerRegistry.unregister(this);
140 providerService = null;
141 scheduledTask.cancel(true);
142 executor.shutdown();
143 log.info("Stopped");
144 }
145
146 @Modified
147 public void modified(ComponentContext context) {
148 if (context == null) {
149 log.info("No component configuration");
150 return;
151 } else {
152 Dictionary<?, ?> properties = context.getProperties();
153
154 int newPollFrequency = getNewPollFrequency(properties, linkPollFrequencySeconds);
155 int newPollDelay = getNewPollDealy(properties, linkPollDelaySeconds);
156 if (newPollFrequency != linkPollFrequencySeconds ||
157 newPollDelay != linkPollDelaySeconds) {
158 linkPollFrequencySeconds = newPollFrequency;
159 linkPollDelaySeconds = newPollDelay;
160 //stops the old scheduled task
161 scheduledTask.cancel(true);
162 //schedules new task at the new polling rate
163 scheduledTask = schedulePolling();
164 }
165 }
166 log.info("Modified");
167 }
168
169 private int getNewPollFrequency(Dictionary<?, ?> properties, int pollFrequency) {
170 int newPollFrequency;
171 try {
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700172 String s = get(properties, POLL_FREQUENCY_SECONDS);
Michele Santuari208b1672016-08-04 17:33:12 +0200173 newPollFrequency = isNullOrEmpty(s) ? pollFrequency : Integer.parseInt(s.trim());
174 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700175 newPollFrequency = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200176 }
177 return newPollFrequency;
178 }
179
180 private int getNewPollDealy(Dictionary<?, ?> properties, int pollDelay) {
181 int newPollFrequency;
182 try {
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700183 String s = get(properties, POLL_DELAY_SECONDS);
Michele Santuari208b1672016-08-04 17:33:12 +0200184 newPollFrequency = isNullOrEmpty(s) ? pollDelay : Integer.parseInt(s.trim());
185 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700186 newPollFrequency = POLL_DELAY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200187 }
188 return newPollFrequency;
189 }
190
191 private ScheduledFuture schedulePolling() {
192 return executor.scheduleAtFixedRate(this::discoverLinksTasks,
193 linkPollDelaySeconds,
194 linkPollFrequencySeconds,
195 SECONDS);
196 }
197
198 private void discoverLinksTasks() {
199 deviceService.getAvailableDevices().forEach(device -> {
200 if (isSupported(device)) {
201 evaluateLinks(device.id(), device.as(LinkDiscovery.class).getLinks());
202 }
203 });
204 }
205
206 private void evaluateLinks(DeviceId deviceId, Set<LinkDescription> discoveredLinksDesc) {
Jose Miguel Guzmand4499a32016-10-31 09:21:08 -0300207 if (discoveredLinksDesc == null) {
208 return;
209 }
210
Michele Santuari208b1672016-08-04 17:33:12 +0200211 //The provider will get only existing links related to LinkDiscovery
DongRyeol Cha9547fc12018-07-24 15:26:16 +0900212 Set<Link> storedLinks = linkService.getDeviceIngressLinks(deviceId)
Michele Santuari208b1672016-08-04 17:33:12 +0200213 .stream()
214 .filter(link -> {
215 String value = link.annotations().value(AnnotationKeys.PROTOCOL);
216 if (value != null && value.equals(SCHEME_NAME.toUpperCase())) {
217 return true;
218 }
219 return false;
220 })
221 .collect(Collectors.toSet());
222
223 //Convert Link to LinkDescription for comparison
224 Set<LinkDescription> storedLinkDescs = new HashSet<>();
225 storedLinks.forEach(link -> storedLinkDescs
226 .add(new DefaultLinkDescription(
227 link.src(), link.dst(), link.type(), link.isExpected(),
228 DefaultAnnotations.builder().putAll(link.annotations()).build())));
229 log.debug("Current stored links provider related {}", storedLinks);
230
231 //Add the correct annotation for comparison
232 Set<LinkDescription> discoveredLinkDescsAnn = new HashSet<>();
233
234 discoveredLinksDesc.forEach(linkDesc -> discoveredLinkDescsAnn
235 .add(new DefaultLinkDescription(
236 linkDesc.src(), linkDesc.dst(), linkDesc.type(), false,
237 DefaultAnnotations.builder().putAll(linkDesc.annotations())
238 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
239 .build())));
240
241 Set<LinkDescription> linkDescsToBeRemoved = new HashSet<>(storedLinkDescs);
242 linkDescsToBeRemoved.removeAll(discoveredLinkDescsAnn);
243 log.debug("Links to be removed {}", linkDescsToBeRemoved);
244 linkDescsToBeRemoved.forEach(linkDesc ->
245 providerService.linkVanished(linkDesc));
246
247 Set<LinkDescription> linksToBeAdded = new HashSet<>(discoveredLinkDescsAnn);
248 linksToBeAdded.removeAll(storedLinkDescs);
249 log.debug("Links to be added {}", linksToBeAdded);
250 linksToBeAdded.forEach(linkDesc -> providerService.linkDetected(linkDesc)
251 );
252 }
253
254 protected boolean isSupported(Device device) {
255 boolean supported = mastershipService.isLocalMaster(device.id())
256 && device.is(LinkDiscovery.class);
257 if (!supported) {
258 log.debug("Device {} does not support LinkDiscovery", device);
259 }
260 return supported;
261 }
262
263 /**
264 * Listener for core device events.
265 */
266 private class InternalDeviceListener implements DeviceListener {
267 @Override
268 public void event(DeviceEvent event) {
269 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
270 executor.execute(() -> event.subject().as(LinkDiscovery.class).getLinks()
271 .forEach(linkDesc -> {
272 providerService.linkDetected(new DefaultLinkDescription(
273 linkDesc.src(), linkDesc.dst(), linkDesc.type(), false,
274 DefaultAnnotations.builder()
275 .putAll(linkDesc.annotations())
276 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
277 .build()));
278 }));
279 }
280 }
281
282 @Override
283 public boolean isRelevant(DeviceEvent event) {
284 return isSupported(event.subject());
285 }
286 }
287}