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