blob: 44dd60dd014e3e35abf8f4930ebe19ce1ce276b8 [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 Vachuska4167c3f2018-10-16 07:16:31 -070061import static org.onosproject.provider.linkdiscovery.impl.OsgiPropertyDefaults.POLL_DELAY_SECONDS_DEFAULT;
62import static org.onosproject.provider.linkdiscovery.impl.OsgiPropertyDefaults.POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +020063import static org.slf4j.LoggerFactory.getLogger;
64
65/**
66 * Link provider capable of polling the environment using the device driver
67 * {@link LinkDiscovery} behaviour.
68 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070069@Component(immediate = true,
70 property = {
71 "linkPollDelaySeconds:Integer=" + POLL_DELAY_SECONDS_DEFAULT,
72 "linkPollFrequencySeconds:Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
73 })
Michele Santuari208b1672016-08-04 17:33:12 +020074public class LinkDiscoveryProvider extends AbstractProvider
75 implements LinkProvider {
76
77 protected static final String APP_NAME = "org.onosproject.linkdiscovery";
78 protected static final String SCHEME_NAME = "linkdiscovery";
79 private static final String LINK_PROVIDER_PACKAGE = "org.onosproject.provider.linkdiscovery";
80 private final Logger log = getLogger(getClass());
Ray Milkeyd84f89b2018-08-17 14:54:17 -070081 //@Property(name = "linkPollDelaySeconds", intValue = DEFAULT_POLL_DELAY_SECONDS,
82 // label = "Initial delay (in seconds) for polling link discovery")
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070083 protected static int linkPollDelaySeconds = POLL_DELAY_SECONDS_DEFAULT;
84
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085 //@Property(name = "linkPollFrequencySeconds", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
86 // label = "Frequency (in seconds) for polling link discovery")
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070087 protected static int linkPollFrequencySeconds = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +020088
89
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020091 protected LinkProviderRegistry providerRegistry;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020093 protected CoreService coreService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070094 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020095 protected LinkService linkService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020097 protected MastershipService mastershipService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +020099 protected DeviceService deviceService;
100 protected ScheduledExecutorService executor =
101 newScheduledThreadPool(2, groupedThreads("onos/netconf-link",
102 "discovery-%d"));
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Michele Santuari208b1672016-08-04 17:33:12 +0200104 protected ComponentConfigService cfgService;
105
106 protected LinkProviderService providerService;
107 private InternalDeviceListener deviceListener = new InternalDeviceListener();
108 private ApplicationId appId;
109 private ScheduledFuture<?> scheduledTask;
110
111 /**
112 * Creates a provider with the supplied identifier.
113 */
114 public LinkDiscoveryProvider() {
115 super(new ProviderId(SCHEME_NAME, LINK_PROVIDER_PACKAGE));
116 }
117
118 @Activate
119 public void activate(ComponentContext context) {
120 providerService = providerRegistry.register(this);
121 appId = coreService.registerApplication(APP_NAME);
122 deviceService.addListener(deviceListener);
123 cfgService.registerProperties(getClass());
124
125 if (context == null) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700126 linkPollFrequencySeconds = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200127 log.info("No component configuration");
128 } else {
129 Dictionary<?, ?> properties = context.getProperties();
130 linkPollFrequencySeconds =
131 getNewPollFrequency(properties, linkPollFrequencySeconds);
132 }
133 scheduledTask = schedulePolling();
134 log.info("Started");
135 }
136
137 @Deactivate
138 public void deactivate() {
139 cfgService.unregisterProperties(getClass(), false);
140 deviceService.removeListener(deviceListener);
141 providerRegistry.unregister(this);
142 providerService = null;
143 scheduledTask.cancel(true);
144 executor.shutdown();
145 log.info("Stopped");
146 }
147
148 @Modified
149 public void modified(ComponentContext context) {
150 if (context == null) {
151 log.info("No component configuration");
152 return;
153 } else {
154 Dictionary<?, ?> properties = context.getProperties();
155
156 int newPollFrequency = getNewPollFrequency(properties, linkPollFrequencySeconds);
157 int newPollDelay = getNewPollDealy(properties, linkPollDelaySeconds);
158 if (newPollFrequency != linkPollFrequencySeconds ||
159 newPollDelay != linkPollDelaySeconds) {
160 linkPollFrequencySeconds = newPollFrequency;
161 linkPollDelaySeconds = newPollDelay;
162 //stops the old scheduled task
163 scheduledTask.cancel(true);
164 //schedules new task at the new polling rate
165 scheduledTask = schedulePolling();
166 }
167 }
168 log.info("Modified");
169 }
170
171 private int getNewPollFrequency(Dictionary<?, ?> properties, int pollFrequency) {
172 int newPollFrequency;
173 try {
174 String s = get(properties, "linkPollFrequencySeconds");
175 newPollFrequency = isNullOrEmpty(s) ? pollFrequency : Integer.parseInt(s.trim());
176 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700177 newPollFrequency = POLL_FREQUENCY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200178 }
179 return newPollFrequency;
180 }
181
182 private int getNewPollDealy(Dictionary<?, ?> properties, int pollDelay) {
183 int newPollFrequency;
184 try {
185 String s = get(properties, "linkPollDelaySeconds");
186 newPollFrequency = isNullOrEmpty(s) ? pollDelay : Integer.parseInt(s.trim());
187 } catch (NumberFormatException | ClassCastException e) {
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700188 newPollFrequency = POLL_DELAY_SECONDS_DEFAULT;
Michele Santuari208b1672016-08-04 17:33:12 +0200189 }
190 return newPollFrequency;
191 }
192
193 private ScheduledFuture schedulePolling() {
194 return executor.scheduleAtFixedRate(this::discoverLinksTasks,
195 linkPollDelaySeconds,
196 linkPollFrequencySeconds,
197 SECONDS);
198 }
199
200 private void discoverLinksTasks() {
201 deviceService.getAvailableDevices().forEach(device -> {
202 if (isSupported(device)) {
203 evaluateLinks(device.id(), device.as(LinkDiscovery.class).getLinks());
204 }
205 });
206 }
207
208 private void evaluateLinks(DeviceId deviceId, Set<LinkDescription> discoveredLinksDesc) {
Jose Miguel Guzmand4499a32016-10-31 09:21:08 -0300209 if (discoveredLinksDesc == null) {
210 return;
211 }
212
Michele Santuari208b1672016-08-04 17:33:12 +0200213 //The provider will get only existing links related to LinkDiscovery
DongRyeol Cha9547fc12018-07-24 15:26:16 +0900214 Set<Link> storedLinks = linkService.getDeviceIngressLinks(deviceId)
Michele Santuari208b1672016-08-04 17:33:12 +0200215 .stream()
216 .filter(link -> {
217 String value = link.annotations().value(AnnotationKeys.PROTOCOL);
218 if (value != null && value.equals(SCHEME_NAME.toUpperCase())) {
219 return true;
220 }
221 return false;
222 })
223 .collect(Collectors.toSet());
224
225 //Convert Link to LinkDescription for comparison
226 Set<LinkDescription> storedLinkDescs = new HashSet<>();
227 storedLinks.forEach(link -> storedLinkDescs
228 .add(new DefaultLinkDescription(
229 link.src(), link.dst(), link.type(), link.isExpected(),
230 DefaultAnnotations.builder().putAll(link.annotations()).build())));
231 log.debug("Current stored links provider related {}", storedLinks);
232
233 //Add the correct annotation for comparison
234 Set<LinkDescription> discoveredLinkDescsAnn = new HashSet<>();
235
236 discoveredLinksDesc.forEach(linkDesc -> discoveredLinkDescsAnn
237 .add(new DefaultLinkDescription(
238 linkDesc.src(), linkDesc.dst(), linkDesc.type(), false,
239 DefaultAnnotations.builder().putAll(linkDesc.annotations())
240 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
241 .build())));
242
243 Set<LinkDescription> linkDescsToBeRemoved = new HashSet<>(storedLinkDescs);
244 linkDescsToBeRemoved.removeAll(discoveredLinkDescsAnn);
245 log.debug("Links to be removed {}", linkDescsToBeRemoved);
246 linkDescsToBeRemoved.forEach(linkDesc ->
247 providerService.linkVanished(linkDesc));
248
249 Set<LinkDescription> linksToBeAdded = new HashSet<>(discoveredLinkDescsAnn);
250 linksToBeAdded.removeAll(storedLinkDescs);
251 log.debug("Links to be added {}", linksToBeAdded);
252 linksToBeAdded.forEach(linkDesc -> providerService.linkDetected(linkDesc)
253 );
254 }
255
256 protected boolean isSupported(Device device) {
257 boolean supported = mastershipService.isLocalMaster(device.id())
258 && device.is(LinkDiscovery.class);
259 if (!supported) {
260 log.debug("Device {} does not support LinkDiscovery", device);
261 }
262 return supported;
263 }
264
265 /**
266 * Listener for core device events.
267 */
268 private class InternalDeviceListener implements DeviceListener {
269 @Override
270 public void event(DeviceEvent event) {
271 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
272 executor.execute(() -> event.subject().as(LinkDiscovery.class).getLinks()
273 .forEach(linkDesc -> {
274 providerService.linkDetected(new DefaultLinkDescription(
275 linkDesc.src(), linkDesc.dst(), linkDesc.type(), false,
276 DefaultAnnotations.builder()
277 .putAll(linkDesc.annotations())
278 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
279 .build()));
280 }));
281 }
282 }
283
284 @Override
285 public boolean isRelevant(DeviceEvent event) {
286 return isSupported(event.subject());
287 }
288 }
289}