blob: d369d027c944f986b4ce5a3eabeccca579799d4a [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 Koshibe8eddc0d2015-02-02 18:07:29 -080022import static org.onlab.util.Tools.toHex;
Ayaka Koshibe0cd42822015-01-22 16:02:31 -080023import static org.onosproject.net.MastershipRole.MASTER;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080024
25import java.util.Dictionary;
26import java.util.List;
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -080027import java.util.Objects;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080028import java.util.concurrent.ConcurrentMap;
29import java.util.concurrent.ExecutorService;
30import java.util.concurrent.Executors;
31import java.util.concurrent.TimeUnit;
32
33import org.apache.felix.scr.annotations.Activate;
34import org.apache.felix.scr.annotations.Component;
35import org.apache.felix.scr.annotations.Deactivate;
Ayaka Koshibe7dc1d042015-01-21 15:28:03 -080036import org.apache.felix.scr.annotations.Modified;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080037import org.apache.felix.scr.annotations.Property;
38import org.apache.felix.scr.annotations.Reference;
39import org.apache.felix.scr.annotations.ReferenceCardinality;
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -080040import org.onosproject.cluster.ClusterService;
41import org.onosproject.cluster.NodeId;
Ayaka Koshibe0cd42822015-01-22 16:02:31 -080042import org.onosproject.mastership.MastershipService;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080043import org.onosproject.net.ConnectPoint;
44import org.onosproject.net.Device;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.Link;
47import org.onosproject.net.PortNumber;
48import org.onosproject.net.device.DeviceEvent;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceService;
51import org.onosproject.net.link.DefaultLinkDescription;
52import org.onosproject.net.link.LinkDescription;
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080053import org.onosproject.net.link.LinkEvent;
54import org.onosproject.net.link.LinkListener;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080055import org.onosproject.net.link.LinkProvider;
56import org.onosproject.net.link.LinkProviderRegistry;
57import org.onosproject.net.link.LinkProviderService;
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080058import org.onosproject.net.link.LinkService;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080059import org.onosproject.net.provider.AbstractProvider;
60import org.onosproject.net.provider.ProviderId;
61import org.osgi.service.component.ComponentContext;
62import org.slf4j.Logger;
63
64import com.google.common.collect.Lists;
65import com.google.common.collect.Maps;
66
67/**
68 * Provider which advertises fake/nonexistent links to the core. To be used for
69 * benchmarking only.
70 */
71@Component(immediate = true)
72public class NullLinkProvider extends AbstractProvider implements LinkProvider {
73
74 private final Logger log = getLogger(getClass());
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected DeviceService deviceService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe0cd42822015-01-22 16:02:31 -080080 protected MastershipService roleService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -080083 protected ClusterService nodeService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe422916f2015-01-15 15:30:23 -080086 protected LinkProviderRegistry providerRegistry;
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080087 private LinkService linkService;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080088
89 private LinkProviderService providerService;
90
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -080091 private static final int DEFAULT_RATE = 0;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080092 // For now, static switch port values
93 private static final PortNumber SRCPORT = PortNumber.portNumber(5);
94 private static final PortNumber DSTPORT = PortNumber.portNumber(6);
95
96 private final InternalLinkProvider linkProvider = new InternalLinkProvider();
Ayaka Koshibe35c71e12015-01-27 17:10:04 -080097 private final InternalLinkListener listener = new InternalLinkListener();
Ayaka Koshibe422916f2015-01-15 15:30:23 -080098
99 // Link descriptions
100 private final ConcurrentMap<ConnectPoint, LinkDescription> descriptions = Maps
101 .newConcurrentMap();
102
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800103 // Local Device ID's that have been seen so far
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800104 private final List<DeviceId> devices = Lists.newArrayList();
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800105 // tail ends of other islands
106 private final List<ConnectPoint> tails = Lists.newArrayList();
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800107
108 private ExecutorService linkDriver = Executors.newFixedThreadPool(1,
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800109 namedThreads("onos-null-link-driver"));
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800110
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800111 // For flicker = true, duration between events in msec.
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800112 @Property(name = "eventRate", value = "0",
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800113 label = "Duration between Link Event")
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800114 private int eventRate = DEFAULT_RATE;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800115
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800116 // For flicker = true, duration between events in msec.
117 @Property(name = "neighbors", value = "",
118 label = "Node ID of instance for neighboring island ")
119 private String neighbor = "";
120
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800121 public NullLinkProvider() {
122 super(new ProviderId("null", "org.onosproject.provider.nil"));
123 }
124
125 @Activate
126 public void activate(ComponentContext context) {
127 providerService = providerRegistry.register(this);
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800128 linkService = (LinkService) providerRegistry;
129 linkService.addListener(listener);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800130 deviceService.addListener(linkProvider);
131 modified(context);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800132 log.info("started");
133 }
134
135 @Deactivate
136 public void deactivate(ComponentContext context) {
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800137 if (eventRate != 0) {
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800138 try {
139 linkDriver.awaitTermination(1000, TimeUnit.MILLISECONDS);
140 } catch (InterruptedException e) {
141 log.error("LinkBuilder did not terminate");
142 }
143 linkDriver.shutdownNow();
144 }
145 deviceService.removeListener(linkProvider);
146 providerRegistry.unregister(this);
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800147 linkService.removeListener(listener);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800148 deviceService = null;
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800149 linkService = null;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800150
151 log.info("stopped");
152 }
153
Ayaka Koshibe7dc1d042015-01-21 15:28:03 -0800154 @Modified
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800155 public void modified(ComponentContext context) {
156 if (context == null) {
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800157 log.info("No configs, using defaults: eventRate={}", DEFAULT_RATE);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800158 return;
159 }
160 Dictionary<?, ?> properties = context.getProperties();
161
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800162 int newRate;
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800163 String newNbor;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800164 try {
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800165 String s = (String) properties.get("eventRate");
Yuta HIGUCHIdd9228d2015-02-10 22:26:59 -0800166 newRate = isNullOrEmpty(s) ? eventRate : Integer.parseInt(s.trim());
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800167 s = (String) properties.get("neighbors");
168 newNbor = isNullOrEmpty(s) ? neighbor : getNeighbor(s.trim());
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800169 } catch (Exception e) {
170 log.warn(e.getMessage());
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800171 newRate = eventRate;
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800172 newNbor = neighbor;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800173 }
174
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800175 if (newNbor != neighbor) {
176 neighbor = newNbor;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800177 }
178
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800179 if (newRate != 0 & eventRate != newRate) {
180 eventRate = newRate;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800181 linkDriver.submit(new LinkDriver());
182 }
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800183
184 log.info("Using new settings: eventRate={}", eventRate);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800185 }
186
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800187 // pick out substring from Deviceid
188 private String part(String devId) {
189 return devId.split(":")[1].substring(12, 16);
190 }
191
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800192 // pick out substring from Deviceid
193 private String nIdPart(String devId) {
194 return devId.split(":")[1].substring(9, 12);
195 }
196
197 // pick out the next node ID in string, return hash (i.e. what's
198 // in a Device ID
199 private String getNeighbor(String nbors) {
200 String me = nodeService.getLocalNode().id().toString();
201 String mynb = "";
202 String[] nodes = nbors.split(",");
203 for (int i = 0; i < nodes.length; i++) {
204 if (i != 0 & nodes[i].equals(me)) {
205 mynb = nodes[i - 1];
206 break;
207 }
208 }
209 // return as hash string.
210 if (!mynb.isEmpty()) {
211 return toHex((Objects.hash(new NodeId(mynb)))).substring(13, 16);
212 }
213 return "";
214 }
215
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800216 /**
217 * Adds links as devices are found, and generates LinkEvents.
218 */
219 private class InternalLinkProvider implements DeviceListener {
220
221 @Override
222 public void event(DeviceEvent event) {
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800223 Device dev = event.subject();
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800224 switch (event.type()) {
225 case DEVICE_ADDED:
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800226 addLink(dev);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800227 break;
228 case DEVICE_REMOVED:
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800229 removeLink(dev);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800230 break;
231 default:
232 break;
233 }
234 }
235
236 private void addLink(Device current) {
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800237 DeviceId did = current.id();
238 if (!MASTER.equals(roleService.getLocalRole(did))) {
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800239
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800240 String part = part(did.toString());
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800241 String npart = nIdPart(did.toString());
Yuta HIGUCHIdd9228d2015-02-10 22:26:59 -0800242 if (part.equals("ffff") && npart.equals(neighbor)) {
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800243 // 'tail' of our neighboring island - link us <- tail
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800244 tails.add(new ConnectPoint(did, SRCPORT));
245 }
246 tryLinkTail();
247 return;
248 }
249 devices.add(did);
250
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800251 if (devices.size() == 1) {
252 return;
253 }
254
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800255 // Normal flow - attach new device to the last-seen device
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800256 DeviceId prev = devices.get(devices.size() - 2);
257 ConnectPoint src = new ConnectPoint(prev, SRCPORT);
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800258 ConnectPoint dst = new ConnectPoint(did, DSTPORT);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800259
260 LinkDescription fdesc = new DefaultLinkDescription(src, dst,
261 Link.Type.DIRECT);
262 LinkDescription rdesc = new DefaultLinkDescription(dst, src,
263 Link.Type.DIRECT);
264 descriptions.put(src, fdesc);
265 descriptions.put(dst, rdesc);
266
267 providerService.linkDetected(fdesc);
268 providerService.linkDetected(rdesc);
269 }
270
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800271 // try to link to a tail to first element
272 private void tryLinkTail() {
273 if (tails.isEmpty() || devices.isEmpty()) {
274 return;
275 }
276 ConnectPoint first = new ConnectPoint(devices.get(0), DSTPORT);
277 boolean added = false;
278 for (ConnectPoint cp : tails) {
279 if (!linkService.getLinks(cp).isEmpty()) {
280 continue;
281 }
282 LinkDescription ld = new DefaultLinkDescription(cp, first,
283 Link.Type.DIRECT);
284 descriptions.put(cp, ld);
285 providerService.linkDetected(ld);
286 added = true;
287 break;
288 }
289 if (added) {
290 tails.clear();
291 }
292 }
293
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800294 private void removeLink(Device device) {
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800295 if (!MASTER.equals(roleService.getLocalRole(device.id()))) {
296 return;
297 }
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800298 providerService.linksVanished(device.id());
299 devices.remove(device.id());
300 }
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800301
302 }
303
304 private class InternalLinkListener implements LinkListener {
305
306 @Override
307 public void event(LinkEvent event) {
308 switch (event.type()) {
309 case LINK_ADDED:
310 // If a link from another island, cast one back.
311 DeviceId sdid = event.subject().src().deviceId();
312 PortNumber pn = event.subject().src().port();
313
314 if (roleService.getLocalRole(sdid).equals(MASTER)) {
315 String part = part(sdid.toString());
316 if (part.equals("ffff") && SRCPORT.equals(pn)) {
317 LinkDescription ld = new DefaultLinkDescription(event
318 .subject().dst(), event.subject().src(),
319 Link.Type.DIRECT);
320 descriptions.put(event.subject().dst(), ld);
321 providerService.linkDetected(ld);
322 }
323 return;
324 }
325 break;
326 default:
327 break;
328 }
329 }
330
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800331 }
332
333 /**
334 * Generates link events using fake links.
335 */
336 private class LinkDriver implements Runnable {
337
338 @Override
339 public void run() {
340 while (!linkDriver.isShutdown()) {
341 for (LinkDescription desc : descriptions.values()) {
342 providerService.linkVanished(desc);
343 delay(eventRate);
344 providerService.linkDetected(desc);
345 delay(eventRate);
346 }
347 }
348 }
349 }
350}