blob: 7dc6f068d9913dd1c1af6b8582c57d78c8e02939 [file] [log] [blame]
Michele Santuari208b1672016-08-04 17:33:12 +02001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
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
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onosproject.cfg.ComponentConfigService;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.mastership.MastershipService;
30import org.onosproject.net.AnnotationKeys;
31import org.onosproject.net.DefaultAnnotations;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Link;
35import org.onosproject.net.behaviour.LinkDiscovery;
36import org.onosproject.net.device.DeviceEvent;
37import org.onosproject.net.device.DeviceListener;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.link.DefaultLinkDescription;
40import org.onosproject.net.link.LinkDescription;
41import org.onosproject.net.link.LinkProvider;
42import org.onosproject.net.link.LinkProviderRegistry;
43import org.onosproject.net.link.LinkProviderService;
44import org.onosproject.net.link.LinkService;
45import org.onosproject.net.provider.AbstractProvider;
46import org.onosproject.net.provider.ProviderId;
47import org.osgi.service.component.ComponentContext;
48import org.slf4j.Logger;
49
50import java.util.Dictionary;
51import java.util.HashSet;
52import java.util.Set;
53import java.util.concurrent.ScheduledExecutorService;
54import java.util.concurrent.ScheduledFuture;
55import java.util.stream.Collectors;
56
57import static com.google.common.base.Strings.isNullOrEmpty;
58import static java.util.concurrent.Executors.newScheduledThreadPool;
59import static java.util.concurrent.TimeUnit.SECONDS;
60import static org.onlab.util.Tools.get;
61import static org.onlab.util.Tools.groupedThreads;
62import static org.slf4j.LoggerFactory.getLogger;
63
64/**
65 * Link provider capable of polling the environment using the device driver
66 * {@link LinkDiscovery} behaviour.
67 */
68@Component(immediate = true)
69public class LinkDiscoveryProvider extends AbstractProvider
70 implements LinkProvider {
71
72 protected static final String APP_NAME = "org.onosproject.linkdiscovery";
73 protected static final String SCHEME_NAME = "linkdiscovery";
74 private static final String LINK_PROVIDER_PACKAGE = "org.onosproject.provider.linkdiscovery";
75 private final Logger log = getLogger(getClass());
76 private static final int DEFAULT_POLL_DELAY_SECONDS = 20;
77 @Property(name = "linkPollDelaySeconds", intValue = DEFAULT_POLL_DELAY_SECONDS,
78 label = "Initial delay (in seconds) for polling link discovery")
79 protected static int linkPollDelaySeconds = DEFAULT_POLL_DELAY_SECONDS;
80 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 10;
81 @Property(name = "linkPollFrequencySeconds", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
82 label = "Frequency (in seconds) for polling link discovery")
83 protected static int linkPollFrequencySeconds = DEFAULT_POLL_FREQUENCY_SECONDS;
84
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected LinkProviderRegistry providerRegistry;
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected CoreService coreService;
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected LinkService linkService;
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected MastershipService mastershipService;
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DeviceService deviceService;
96 protected ScheduledExecutorService executor =
97 newScheduledThreadPool(2, groupedThreads("onos/netconf-link",
98 "discovery-%d"));
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected ComponentConfigService cfgService;
101
102 protected LinkProviderService providerService;
103 private InternalDeviceListener deviceListener = new InternalDeviceListener();
104 private ApplicationId appId;
105 private ScheduledFuture<?> scheduledTask;
106
107 /**
108 * Creates a provider with the supplied identifier.
109 */
110 public LinkDiscoveryProvider() {
111 super(new ProviderId(SCHEME_NAME, LINK_PROVIDER_PACKAGE));
112 }
113
114 @Activate
115 public void activate(ComponentContext context) {
116 providerService = providerRegistry.register(this);
117 appId = coreService.registerApplication(APP_NAME);
118 deviceService.addListener(deviceListener);
119 cfgService.registerProperties(getClass());
120
121 if (context == null) {
122 linkPollFrequencySeconds = DEFAULT_POLL_FREQUENCY_SECONDS;
123 log.info("No component configuration");
124 } else {
125 Dictionary<?, ?> properties = context.getProperties();
126 linkPollFrequencySeconds =
127 getNewPollFrequency(properties, linkPollFrequencySeconds);
128 }
129 scheduledTask = schedulePolling();
130 log.info("Started");
131 }
132
133 @Deactivate
134 public void deactivate() {
135 cfgService.unregisterProperties(getClass(), false);
136 deviceService.removeListener(deviceListener);
137 providerRegistry.unregister(this);
138 providerService = null;
139 scheduledTask.cancel(true);
140 executor.shutdown();
141 log.info("Stopped");
142 }
143
144 @Modified
145 public void modified(ComponentContext context) {
146 if (context == null) {
147 log.info("No component configuration");
148 return;
149 } else {
150 Dictionary<?, ?> properties = context.getProperties();
151
152 int newPollFrequency = getNewPollFrequency(properties, linkPollFrequencySeconds);
153 int newPollDelay = getNewPollDealy(properties, linkPollDelaySeconds);
154 if (newPollFrequency != linkPollFrequencySeconds ||
155 newPollDelay != linkPollDelaySeconds) {
156 linkPollFrequencySeconds = newPollFrequency;
157 linkPollDelaySeconds = newPollDelay;
158 //stops the old scheduled task
159 scheduledTask.cancel(true);
160 //schedules new task at the new polling rate
161 scheduledTask = schedulePolling();
162 }
163 }
164 log.info("Modified");
165 }
166
167 private int getNewPollFrequency(Dictionary<?, ?> properties, int pollFrequency) {
168 int newPollFrequency;
169 try {
170 String s = get(properties, "linkPollFrequencySeconds");
171 newPollFrequency = isNullOrEmpty(s) ? pollFrequency : Integer.parseInt(s.trim());
172 } catch (NumberFormatException | ClassCastException e) {
173 newPollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
174 }
175 return newPollFrequency;
176 }
177
178 private int getNewPollDealy(Dictionary<?, ?> properties, int pollDelay) {
179 int newPollFrequency;
180 try {
181 String s = get(properties, "linkPollDelaySeconds");
182 newPollFrequency = isNullOrEmpty(s) ? pollDelay : Integer.parseInt(s.trim());
183 } catch (NumberFormatException | ClassCastException e) {
184 newPollFrequency = DEFAULT_POLL_DELAY_SECONDS;
185 }
186 return newPollFrequency;
187 }
188
189 private ScheduledFuture schedulePolling() {
190 return executor.scheduleAtFixedRate(this::discoverLinksTasks,
191 linkPollDelaySeconds,
192 linkPollFrequencySeconds,
193 SECONDS);
194 }
195
196 private void discoverLinksTasks() {
197 deviceService.getAvailableDevices().forEach(device -> {
198 if (isSupported(device)) {
199 evaluateLinks(device.id(), device.as(LinkDiscovery.class).getLinks());
200 }
201 });
202 }
203
204 private void evaluateLinks(DeviceId deviceId, Set<LinkDescription> discoveredLinksDesc) {
Jose Miguel Guzmand4499a32016-10-31 09:21:08 -0300205 if (discoveredLinksDesc == null) {
206 return;
207 }
208
Michele Santuari208b1672016-08-04 17:33:12 +0200209 //The provider will get only existing links related to LinkDiscovery
210 Set<Link> storedLinks = linkService.getDeviceEgressLinks(deviceId)
211 .stream()
212 .filter(link -> {
213 String value = link.annotations().value(AnnotationKeys.PROTOCOL);
214 if (value != null && value.equals(SCHEME_NAME.toUpperCase())) {
215 return true;
216 }
217 return false;
218 })
219 .collect(Collectors.toSet());
220
221 //Convert Link to LinkDescription for comparison
222 Set<LinkDescription> storedLinkDescs = new HashSet<>();
223 storedLinks.forEach(link -> storedLinkDescs
224 .add(new DefaultLinkDescription(
225 link.src(), link.dst(), link.type(), link.isExpected(),
226 DefaultAnnotations.builder().putAll(link.annotations()).build())));
227 log.debug("Current stored links provider related {}", storedLinks);
228
229 //Add the correct annotation for comparison
230 Set<LinkDescription> discoveredLinkDescsAnn = new HashSet<>();
231
232 discoveredLinksDesc.forEach(linkDesc -> discoveredLinkDescsAnn
233 .add(new DefaultLinkDescription(
234 linkDesc.src(), linkDesc.dst(), linkDesc.type(), false,
235 DefaultAnnotations.builder().putAll(linkDesc.annotations())
236 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
237 .build())));
238
239 Set<LinkDescription> linkDescsToBeRemoved = new HashSet<>(storedLinkDescs);
240 linkDescsToBeRemoved.removeAll(discoveredLinkDescsAnn);
241 log.debug("Links to be removed {}", linkDescsToBeRemoved);
242 linkDescsToBeRemoved.forEach(linkDesc ->
243 providerService.linkVanished(linkDesc));
244
245 Set<LinkDescription> linksToBeAdded = new HashSet<>(discoveredLinkDescsAnn);
246 linksToBeAdded.removeAll(storedLinkDescs);
247 log.debug("Links to be added {}", linksToBeAdded);
248 linksToBeAdded.forEach(linkDesc -> providerService.linkDetected(linkDesc)
249 );
250 }
251
252 protected boolean isSupported(Device device) {
253 boolean supported = mastershipService.isLocalMaster(device.id())
254 && device.is(LinkDiscovery.class);
255 if (!supported) {
256 log.debug("Device {} does not support LinkDiscovery", device);
257 }
258 return supported;
259 }
260
261 /**
262 * Listener for core device events.
263 */
264 private class InternalDeviceListener implements DeviceListener {
265 @Override
266 public void event(DeviceEvent event) {
267 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
268 executor.execute(() -> event.subject().as(LinkDiscovery.class).getLinks()
269 .forEach(linkDesc -> {
270 providerService.linkDetected(new DefaultLinkDescription(
271 linkDesc.src(), linkDesc.dst(), linkDesc.type(), false,
272 DefaultAnnotations.builder()
273 .putAll(linkDesc.annotations())
274 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
275 .build()));
276 }));
277 }
278 }
279
280 @Override
281 public boolean isRelevant(DeviceEvent event) {
282 return isSupported(event.subject());
283 }
284 }
285}