blob: 99e46dca7fd98962bccd99b64c06c3113f8dd80b [file] [log] [blame]
Ayaka Koshibe422916f2015-01-15 15:30:23 -08001/*
2 * Copyright 2015 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 */
16package org.onosproject.provider.nil.link.impl;
17
18import static com.google.common.base.Strings.isNullOrEmpty;
19import static org.onlab.util.Tools.delay;
20import static org.onlab.util.Tools.namedThreads;
21import static org.slf4j.LoggerFactory.getLogger;
Ayaka Koshibe0cd42822015-01-22 16:02:31 -080022import static org.onosproject.net.MastershipRole.MASTER;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080023
24import java.util.Dictionary;
25import java.util.List;
26import java.util.concurrent.ConcurrentMap;
27import java.util.concurrent.ExecutorService;
28import java.util.concurrent.Executors;
29import java.util.concurrent.TimeUnit;
30
31import org.apache.felix.scr.annotations.Activate;
32import org.apache.felix.scr.annotations.Component;
33import org.apache.felix.scr.annotations.Deactivate;
Ayaka Koshibe7dc1d042015-01-21 15:28:03 -080034import org.apache.felix.scr.annotations.Modified;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080035import org.apache.felix.scr.annotations.Property;
36import org.apache.felix.scr.annotations.Reference;
37import org.apache.felix.scr.annotations.ReferenceCardinality;
Ayaka Koshibe0cd42822015-01-22 16:02:31 -080038import org.onosproject.mastership.MastershipService;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080039import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.Link;
43import org.onosproject.net.PortNumber;
44import org.onosproject.net.device.DeviceEvent;
45import org.onosproject.net.device.DeviceListener;
46import org.onosproject.net.device.DeviceService;
47import org.onosproject.net.link.DefaultLinkDescription;
48import org.onosproject.net.link.LinkDescription;
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080049import org.onosproject.net.link.LinkEvent;
50import org.onosproject.net.link.LinkListener;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080051import org.onosproject.net.link.LinkProvider;
52import org.onosproject.net.link.LinkProviderRegistry;
53import org.onosproject.net.link.LinkProviderService;
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080054import org.onosproject.net.link.LinkService;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080055import org.onosproject.net.provider.AbstractProvider;
56import org.onosproject.net.provider.ProviderId;
57import org.osgi.service.component.ComponentContext;
58import org.slf4j.Logger;
59
60import com.google.common.collect.Lists;
61import com.google.common.collect.Maps;
62
63/**
64 * Provider which advertises fake/nonexistent links to the core. To be used for
65 * benchmarking only.
66 */
67@Component(immediate = true)
68public class NullLinkProvider extends AbstractProvider implements LinkProvider {
69
70 private final Logger log = getLogger(getClass());
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected DeviceService deviceService;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe0cd42822015-01-22 16:02:31 -080076 protected MastershipService roleService;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe422916f2015-01-15 15:30:23 -080079 protected LinkProviderRegistry providerRegistry;
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080080 private LinkService linkService;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080081
82 private LinkProviderService providerService;
83
84 private static final boolean FLICKER = false;
85 private static final int DEFAULT_RATE = 3000;
86 // For now, static switch port values
87 private static final PortNumber SRCPORT = PortNumber.portNumber(5);
88 private static final PortNumber DSTPORT = PortNumber.portNumber(6);
89
90 private final InternalLinkProvider linkProvider = new InternalLinkProvider();
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080091 private final InternalLinkListener listener = new InternalLinkListener();
Ayaka Koshibe422916f2015-01-15 15:30:23 -080092
93 // Link descriptions
94 private final ConcurrentMap<ConnectPoint, LinkDescription> descriptions = Maps
95 .newConcurrentMap();
96
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080097 // Local Device ID's that have been seen so far
Ayaka Koshibe422916f2015-01-15 15:30:23 -080098 private final List<DeviceId> devices = Lists.newArrayList();
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080099 // tail ends of other islands
100 private final List<ConnectPoint> tails = Lists.newArrayList();
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800101
102 private ExecutorService linkDriver = Executors.newFixedThreadPool(1,
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800103 namedThreads("onos-null-link-driver"));
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800104
105 // If true, 'flickers' links by alternating link up/down events at eventRate
Ayaka Koshibe2a85e842015-01-29 15:39:40 -0800106 @Property(name = "flicker", value = "false",
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800107 label = "Setting to flap links")
108 private boolean flicker = FLICKER;
109
110 // For flicker = true, duration between events in msec.
Ayaka Koshibe2a85e842015-01-29 15:39:40 -0800111 @Property(name = "eventRate", value = "3000",
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800112 label = "Duration between Link Event")
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800113 private int eventRate = DEFAULT_RATE;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800114
115 public NullLinkProvider() {
116 super(new ProviderId("null", "org.onosproject.provider.nil"));
117 }
118
119 @Activate
120 public void activate(ComponentContext context) {
121 providerService = providerRegistry.register(this);
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800122 linkService = (LinkService) providerRegistry;
123 linkService.addListener(listener);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800124 deviceService.addListener(linkProvider);
125 modified(context);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800126 log.info("started");
127 }
128
129 @Deactivate
130 public void deactivate(ComponentContext context) {
131 if (flicker) {
132 try {
133 linkDriver.awaitTermination(1000, TimeUnit.MILLISECONDS);
134 } catch (InterruptedException e) {
135 log.error("LinkBuilder did not terminate");
136 }
137 linkDriver.shutdownNow();
138 }
139 deviceService.removeListener(linkProvider);
140 providerRegistry.unregister(this);
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800141 linkService.removeListener(listener);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800142 deviceService = null;
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800143 linkService = null;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800144
145 log.info("stopped");
146 }
147
Ayaka Koshibe7dc1d042015-01-21 15:28:03 -0800148 @Modified
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800149 public void modified(ComponentContext context) {
150 if (context == null) {
151 log.info("No configs, using defaults: flicker={}, eventRate={}",
152 FLICKER, DEFAULT_RATE);
153 return;
154 }
155 Dictionary<?, ?> properties = context.getProperties();
156
157 boolean flickSetting;
158 int newRate;
159 try {
160 String s = (String) properties.get("flicker");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800161 flickSetting = isNullOrEmpty(s) ? flicker : Boolean.valueOf(s.trim());
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800162 s = (String) properties.get("eventRate");
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800163 newRate = isNullOrEmpty(s) ? eventRate : Integer.valueOf(s.trim());
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800164 } catch (Exception e) {
165 log.warn(e.getMessage());
166 flickSetting = flicker;
167 newRate = eventRate;
168 }
169
170 if (flicker != flickSetting) {
171 flicker = flickSetting;
172 }
173
174 if (flicker) {
175 if (eventRate != newRate) {
176 eventRate = newRate;
177 }
178 linkDriver.submit(new LinkDriver());
179 }
180 log.info("Using new settings: flicker={}, eventRate={}", flicker,
181 eventRate);
182 }
183
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800184 // pick out substring from Deviceid
185 private String part(String devId) {
186 return devId.split(":")[1].substring(12, 16);
187 }
188
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800189 /**
190 * Adds links as devices are found, and generates LinkEvents.
191 */
192 private class InternalLinkProvider implements DeviceListener {
193
194 @Override
195 public void event(DeviceEvent event) {
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800196 Device dev = event.subject();
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800197 switch (event.type()) {
198 case DEVICE_ADDED:
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800199 addLink(dev);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800200 break;
201 case DEVICE_REMOVED:
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800202 removeLink(dev);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800203 break;
204 default:
205 break;
206 }
207 }
208
209 private void addLink(Device current) {
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800210 DeviceId did = current.id();
211 if (!MASTER.equals(roleService.getLocalRole(did))) {
212 String part = part(did.toString());
213 if (part.equals("ffff")) {
214 // 'tail' of an island - link us <- tail
215 tails.add(new ConnectPoint(did, SRCPORT));
216 }
217 tryLinkTail();
218 return;
219 }
220 devices.add(did);
221
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800222 if (devices.size() == 1) {
223 return;
224 }
225
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800226 // Normal flow - attach new device to the last-seen device
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800227 DeviceId prev = devices.get(devices.size() - 2);
228 ConnectPoint src = new ConnectPoint(prev, SRCPORT);
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800229 ConnectPoint dst = new ConnectPoint(did, DSTPORT);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800230
231 LinkDescription fdesc = new DefaultLinkDescription(src, dst,
232 Link.Type.DIRECT);
233 LinkDescription rdesc = new DefaultLinkDescription(dst, src,
234 Link.Type.DIRECT);
235 descriptions.put(src, fdesc);
236 descriptions.put(dst, rdesc);
237
238 providerService.linkDetected(fdesc);
239 providerService.linkDetected(rdesc);
240 }
241
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800242 // try to link to a tail to first element
243 private void tryLinkTail() {
244 if (tails.isEmpty() || devices.isEmpty()) {
245 return;
246 }
247 ConnectPoint first = new ConnectPoint(devices.get(0), DSTPORT);
248 boolean added = false;
249 for (ConnectPoint cp : tails) {
250 if (!linkService.getLinks(cp).isEmpty()) {
251 continue;
252 }
253 LinkDescription ld = new DefaultLinkDescription(cp, first,
254 Link.Type.DIRECT);
255 descriptions.put(cp, ld);
256 providerService.linkDetected(ld);
257 added = true;
258 break;
259 }
260 if (added) {
261 tails.clear();
262 }
263 }
264
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800265 private void removeLink(Device device) {
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800266 if (!MASTER.equals(roleService.getLocalRole(device.id()))) {
267 return;
268 }
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800269 providerService.linksVanished(device.id());
270 devices.remove(device.id());
271 }
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800272
273 }
274
275 private class InternalLinkListener implements LinkListener {
276
277 @Override
278 public void event(LinkEvent event) {
279 switch (event.type()) {
280 case LINK_ADDED:
281 // If a link from another island, cast one back.
282 DeviceId sdid = event.subject().src().deviceId();
283 PortNumber pn = event.subject().src().port();
284
285 if (roleService.getLocalRole(sdid).equals(MASTER)) {
286 String part = part(sdid.toString());
287 if (part.equals("ffff") && SRCPORT.equals(pn)) {
288 LinkDescription ld = new DefaultLinkDescription(event
289 .subject().dst(), event.subject().src(),
290 Link.Type.DIRECT);
291 descriptions.put(event.subject().dst(), ld);
292 providerService.linkDetected(ld);
293 }
294 return;
295 }
296 break;
297 default:
298 break;
299 }
300 }
301
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800302 }
303
304 /**
305 * Generates link events using fake links.
306 */
307 private class LinkDriver implements Runnable {
308
309 @Override
310 public void run() {
311 while (!linkDriver.isShutdown()) {
312 for (LinkDescription desc : descriptions.values()) {
313 providerService.linkVanished(desc);
314 delay(eventRate);
315 providerService.linkDetected(desc);
316 delay(eventRate);
317 }
318 }
319 }
320 }
321}