blob: a05f83e4a92d02e8fd2a15dc97c1221cd81bed57 [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
Ayaka Koshibeef69be32015-03-15 22:29:19 -070018import com.google.common.base.Charsets;
Ayaka Koshibe13f896f2015-03-10 15:01:44 -070019import com.google.common.collect.HashMultimap;
Ayaka Koshibe9209ea22015-03-09 10:57:50 -070020import com.google.common.collect.Lists;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080021import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
Ayaka Koshibeef69be32015-03-15 22:29:19 -070023import com.google.common.io.Files;
24
Ayaka Koshibe422916f2015-01-15 15:30:23 -080025import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
Ayaka Koshibe7dc1d042015-01-21 15:28:03 -080028import org.apache.felix.scr.annotations.Modified;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080029import org.apache.felix.scr.annotations.Property;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070032import org.onosproject.cfg.ComponentConfigService;
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -080033import org.onosproject.cluster.ClusterService;
34import org.onosproject.cluster.NodeId;
Ayaka Koshibe0cd42822015-01-22 16:02:31 -080035import org.onosproject.mastership.MastershipService;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080036import org.onosproject.net.ConnectPoint;
37import org.onosproject.net.Device;
38import org.onosproject.net.DeviceId;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080039import org.onosproject.net.PortNumber;
40import org.onosproject.net.device.DeviceEvent;
41import org.onosproject.net.device.DeviceListener;
42import org.onosproject.net.device.DeviceService;
43import org.onosproject.net.link.DefaultLinkDescription;
44import org.onosproject.net.link.LinkDescription;
45import org.onosproject.net.link.LinkProvider;
46import org.onosproject.net.link.LinkProviderRegistry;
47import org.onosproject.net.link.LinkProviderService;
48import org.onosproject.net.provider.AbstractProvider;
49import org.onosproject.net.provider.ProviderId;
50import org.osgi.service.component.ComponentContext;
51import org.slf4j.Logger;
52
Brian O'Connorb34ae262015-03-13 18:59:34 -070053import java.io.BufferedReader;
Ayaka Koshibeef69be32015-03-15 22:29:19 -070054import java.io.File;
Brian O'Connorb34ae262015-03-13 18:59:34 -070055import java.io.IOException;
56import java.net.URI;
57import java.net.URISyntaxException;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080058import java.util.Dictionary;
Ayaka Koshibe9209ea22015-03-09 10:57:50 -070059import java.util.List;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080060import java.util.Set;
61import java.util.concurrent.ConcurrentMap;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080062import java.util.concurrent.Executors;
Ayaka Koshibe9209ea22015-03-09 10:57:50 -070063import java.util.concurrent.ScheduledExecutorService;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080064import java.util.concurrent.TimeUnit;
65
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080066import static org.onlab.util.Tools.groupedThreads;
Ayaka Koshibeef69be32015-03-15 22:29:19 -070067import static org.onlab.util.Tools.get;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080068import static org.onlab.util.Tools.toHex;
Ayaka Koshibe839a8a92015-03-03 17:07:22 -080069import static org.onosproject.net.Link.Type.DIRECT;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080070import static org.slf4j.LoggerFactory.getLogger;
Ayaka Koshibeef69be32015-03-15 22:29:19 -070071import static com.google.common.base.Strings.isNullOrEmpty;
Ayaka Koshibe422916f2015-01-15 15:30:23 -080072
73/**
74 * Provider which advertises fake/nonexistent links to the core. To be used for
75 * benchmarking only.
Ayaka Koshibe839a8a92015-03-03 17:07:22 -080076 *
77 * This provider takes a topology graph file with a DOT-like syntax.
Ayaka Koshibe422916f2015-01-15 15:30:23 -080078 */
79@Component(immediate = true)
80public class NullLinkProvider extends AbstractProvider implements LinkProvider {
81
82 private final Logger log = getLogger(getClass());
83
Ayaka Koshibe9209ea22015-03-09 10:57:50 -070084 // default topology file location and name.
Ayaka Koshibe839a8a92015-03-03 17:07:22 -080085 private static final String CFG_PATH = "/opt/onos/apache-karaf-3.0.2/etc/linkGraph.cfg";
Ayaka Koshibe9209ea22015-03-09 10:57:50 -070086 // default number of workers. Eventually make this tunable
Ayaka Koshibe97940562015-03-11 12:25:46 -070087 private static final int THREADS = (int) Math.max(1, Runtime.getRuntime().availableProcessors() * 0.8);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -080088
Ayaka Koshibe97940562015-03-11 12:25:46 -070089 private static final int CHECK_DURATION = 10; // sec
90 private static final int DEFAULT_RATE = 0; // usec
91 private static final int REFRESH_RATE = 3; // sec
92 // Fake device used for non-flickering thread in deviceMap
Ayaka Koshibe13f896f2015-03-10 15:01:44 -070093 private static final DeviceId DEFAULT = DeviceId.deviceId("null:ffffffffffffffff");
Ayaka Koshibe839a8a92015-03-03 17:07:22 -080094
Ayaka Koshibe422916f2015-01-15 15:30:23 -080095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected DeviceService deviceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe0cd42822015-01-22 16:02:31 -080099 protected MastershipService roleService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800102 protected ClusterService nodeService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800105 protected LinkProviderRegistry providerRegistry;
106
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected ComponentConfigService cfgService;
109
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800110 private LinkProviderService providerService;
111
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800112 private final InternalLinkProvider linkProvider = new InternalLinkProvider();
113
Ayaka Koshibe97940562015-03-11 12:25:46 -0700114 // Mapping between device and drivers that advertise links from device
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700115 private final ConcurrentMap<DeviceId, Set<LinkDriver>> driverMap = Maps
Ayaka Koshibed2c7ad22015-02-13 16:44:07 -0800116 .newConcurrentMap();
117
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800118 // Link descriptions
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700119 private final List<LinkDescription> linkDescrs = Lists.newArrayList();
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800120
Ayaka Koshibe97940562015-03-11 12:25:46 -0700121 // Thread to description map for dividing links amongst threads in flicker mode
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700122 private final List<List<LinkDescription>> linkTasks = Lists.newArrayList();
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700123
124 private ScheduledExecutorService linkDriver =
125 Executors.newScheduledThreadPool(THREADS, groupedThreads("onos/null", "link-driver-%d"));
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800126
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800127 // For flicker = true, duration between events in msec.
Thomas Vachuska3181de32015-03-11 10:07:05 -0700128 @Property(name = "eventRate", intValue = DEFAULT_RATE, label = "Duration between Link Event")
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800129 private int eventRate = DEFAULT_RATE;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800130
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800131 // topology configuration file
Thomas Vachuska3181de32015-03-11 10:07:05 -0700132 @Property(name = "cfgFile", value = CFG_PATH, label = "Topology file location")
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800133 private String cfgFile = CFG_PATH;
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800134
Ayaka Koshibed2c7ad22015-02-13 16:44:07 -0800135 // flag checked to create a LinkDriver, if rate is non-zero.
Ayaka Koshibe97940562015-03-11 12:25:46 -0700136 private volatile boolean flicker = false;
Ayaka Koshibed2c7ad22015-02-13 16:44:07 -0800137
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800138 public NullLinkProvider() {
139 super(new ProviderId("null", "org.onosproject.provider.nil"));
140 }
141
142 @Activate
143 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700144 cfgService.registerProperties(getClass());
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800145 providerService = providerRegistry.register(this);
Ayaka Koshibed2c7ad22015-02-13 16:44:07 -0800146 modified(context);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800147 log.info("started");
148 }
149
150 @Deactivate
151 public void deactivate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700152 cfgService.unregisterProperties(getClass(), false);
Ayaka Koshibed2c7ad22015-02-13 16:44:07 -0800153 linkDriver.shutdown();
154 try {
155 linkDriver.awaitTermination(1000, TimeUnit.MILLISECONDS);
156 } catch (InterruptedException e) {
157 log.error("LinkBuilder did not terminate");
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800158 linkDriver.shutdownNow();
159 }
160 deviceService.removeListener(linkProvider);
161 providerRegistry.unregister(this);
162 deviceService = null;
163
164 log.info("stopped");
165 }
166
Ayaka Koshibe7dc1d042015-01-21 15:28:03 -0800167 @Modified
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800168 public void modified(ComponentContext context) {
169 if (context == null) {
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800170 log.info("No configs, using defaults: eventRate={}", DEFAULT_RATE);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800171 return;
172 }
173 Dictionary<?, ?> properties = context.getProperties();
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800174 int newRate;
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800175 String newPath;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800176 try {
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700177 String s = get(properties, "eventRate");
Ayaka Koshibe97940562015-03-11 12:25:46 -0700178 newRate = isNullOrEmpty(s) ? DEFAULT_RATE : Integer.parseInt(s.trim());
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800179 s = (String) properties.get("cfgFile");
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700180 newPath = isNullOrEmpty(s) ? CFG_PATH : s.trim();
181 } catch (NumberFormatException e) {
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800182 log.warn(e.getMessage());
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800183 newRate = eventRate;
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800184 newPath = cfgFile;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800185 }
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700186 // find/read topology file.
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800187 if (!newPath.equals(cfgFile)) {
188 cfgFile = newPath;
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800189 }
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800190 readGraph(cfgFile, nodeService.getLocalNode().id());
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700191 // check for new eventRate settings.
Ayaka Koshibe97940562015-03-11 12:25:46 -0700192 if (newRate != eventRate) {
193 if (eventRate < 0) {
194 log.warn("Invalid rate, ignoring and using default");
195 eventRate = DEFAULT_RATE;
196 } else {
197 eventRate = newRate;
198 }
199 }
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700200 configureWorkers();
201 log.info("Using settings: eventRate={}, topofile={}", eventRate, cfgFile);
202 }
203
204 // Configures and schedules worker threads based on settings.
205 private void configureWorkers() {
Ayaka Koshibe97940562015-03-11 12:25:46 -0700206 if (eventRate > 0) {
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700207 // now set to 'flicker', previously not flickering
208 if (!flicker) {
Ayaka Koshibe97940562015-03-11 12:25:46 -0700209 flicker = true;
210 allocateLinks();
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700211 // kill off refresh worker for symmetry
212 if (driverMap.containsKey(DEFAULT)) {
213 driverMap.get(DEFAULT).forEach(d -> d.setTasks(Lists.newArrayList()));
214 driverMap.remove(DEFAULT);
215 }
Ayaka Koshibe97940562015-03-11 12:25:46 -0700216 for (int i = 0; i < linkTasks.size(); i++) {
217 List<LinkDescription> links = linkTasks.get(i);
218 LinkDriver driver = new LinkDriver(links);
219 links.forEach(v -> {
220 DeviceId sd = v.src().deviceId();
221 DeviceId dd = v.src().deviceId();
222 driverMap.computeIfAbsent(sd, k -> Sets.newConcurrentHashSet()).add(driver);
223 driverMap.computeIfAbsent(dd, k -> Sets.newConcurrentHashSet()).add(driver);
224 });
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700225 linkDriver.schedule(driver, eventRate, TimeUnit.MICROSECONDS);
Ayaka Koshibe97940562015-03-11 12:25:46 -0700226 }
227 }
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700228 // no need for was flicker since eventRate will be read by workers
Ayaka Koshibe97940562015-03-11 12:25:46 -0700229 } else {
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700230 // now set to 'refresh' was 'flicker' before
Ayaka Koshibe97940562015-03-11 12:25:46 -0700231 if (flicker) {
232 driverMap.forEach((dev, lds) -> lds.forEach(l -> l.deviceRemoved(dev)));
233 driverMap.clear();
234 linkTasks.clear();
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700235 flicker = false;
236 LinkDriver driver = new LinkDriver(linkDescrs);
237 driverMap.computeIfAbsent(DEFAULT, k -> Sets.newConcurrentHashSet()).add(driver);
238 linkDriver.schedule(driver, DEFAULT_RATE, TimeUnit.SECONDS);
239 // was 'refresh' - something changed or we're just starting.
240 } else {
241 if (driverMap.containsKey(DEFAULT)) {
242 driverMap.forEach((dev, ld) -> ld.forEach(d -> d.setTasks(linkDescrs)));
243 return;
244 }
245 LinkDriver driver = new LinkDriver(linkDescrs);
246 driverMap.computeIfAbsent(DEFAULT, k -> Sets.newConcurrentHashSet()).add(driver);
247 linkDriver.schedule(driver, DEFAULT_RATE, TimeUnit.SECONDS);
Ayaka Koshibe97940562015-03-11 12:25:46 -0700248 }
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800249 }
Ayaka Koshibe8eddc0d2015-02-02 18:07:29 -0800250 }
251
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800252 // parse simplified dot-like topology graph
253 private void readGraph(String path, NodeId me) {
254 log.info("path: {}, local: {}", path, me);
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700255 Set<LinkDescription> read = Sets.newHashSet();
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800256 BufferedReader br = null;
257 try {
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700258 br = Files.newReader(new File(path), Charsets.US_ASCII);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800259 String cur = br.readLine();
260 while (cur != null) {
261 if (cur.startsWith("#")) {
262 cur = br.readLine();
263 continue;
264 }
265 String[] parts = cur.trim().split(" ");
266 if (parts.length < 1) {
267 continue;
268 }
269 if (parts[0].equals("graph")) {
270 String node = parts[1].trim();
271 if (node.equals(me.toString())) {
272 cur = br.readLine(); // move to next line, start of links list
273 while (cur != null) {
274 if (cur.trim().contains("}")) {
275 break;
276 }
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700277 readLink(cur.trim().split(" "), me, read);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800278 cur = br.readLine();
279 }
280 } else {
281 while (cur != null) {
282 if (cur.trim().equals("}")) {
283 break;
284 }
285 cur = br.readLine();
286 }
287 }
288 }
289 cur = br.readLine();
290 }
291 } catch (IOException e) {
292 log.warn("Could not find topology file: {}", e);
293 } finally {
294 try {
295 if (br != null) {
296 br.close();
297 }
298 } catch (IOException e) {
299 log.warn("Could not close topology file: {}", e);
300 }
301 }
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700302 synchronized (linkDescrs) {
303 if (!read.isEmpty()) {
304 linkDescrs.clear();
305 linkDescrs.addAll(read);
306 }
307 }
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800308 }
309
310 // parses a link descriptor to make a LinkDescription
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700311 private void readLink(String[] linkArr, NodeId me, Set<LinkDescription> links) {
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800312 if (linkArr[0].startsWith("#")) {
313 return;
314 }
315 if (linkArr.length != 3) {
316 log.warn("Malformed link descriptor:"
317 + " link should be of format src:port [--|->] dst:port,"
318 + " skipping");
319 return;
320 }
321
322 String op = linkArr[1];
323 String[] cp1 = linkArr[0].split(":");
324 String[] cp2 = linkArr[2].split(":");
325
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800326 if (cp1.length != 2 && (cp2.length != 2 || cp2.length != 3)) {
327 log.warn("Malformed endpoint descriptor(s):"
328 + "endpoint format should be DeviceId:port or DeviceId:port:NodeId,"
329 + "skipping");
330 return;
331 }
332 // read in hints about topology.
333 NodeId adj = null;
334 if (cp2.length == 3) {
335 adj = new NodeId(cp2[2]);
336 log.debug("found an island: {}", adj);
337 }
338
339 // reconstruct deviceIDs. Convention is based on NullDeviceProvider.
340 DeviceId sdev = recover(cp1[0], me);
341 DeviceId ddev = (adj == null) ? recover(cp2[0], me) : recover(cp2[0], adj);
342 ConnectPoint src = new ConnectPoint(sdev, PortNumber.portNumber(cp1[1]));
343 ConnectPoint dst = new ConnectPoint(ddev, PortNumber.portNumber(cp2[1]));
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700344 // both link types have incoming half-link
345 LinkDescription in = new DefaultLinkDescription(dst, src, DIRECT);
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700346 links.add(in);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800347 if (op.equals("--")) {
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700348 // bidirectional - within our node's island, make outbound link
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800349 LinkDescription out = new DefaultLinkDescription(src, dst, DIRECT);
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700350 links.add(out);
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700351 log.info("Created bidirectional link: {}, {}", out, in);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800352 } else if (op.equals("->")) {
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800353 log.info("Created unidirectional link: {}", in);
354 } else {
355 log.warn("Unknown link descriptor operand:"
356 + " operand must be '--' or '->', skipping");
357 return;
358 }
359 }
360
361 // recover DeviceId from configs and NodeID
362 private DeviceId recover(String base, NodeId node) {
363 long hash = node.hashCode() << 16;
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700364 int dev = Integer.parseInt(base);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800365 try {
366 return DeviceId.deviceId(new URI("null", toHex(hash | dev), null));
367 } catch (URISyntaxException e) {
368 log.warn("could not create a DeviceID for descriptor {}", dev);
369 return DeviceId.NONE;
370 }
371 }
372
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700373 // adds a LinkDescription to a worker's to-be queue, for flickering
374 private void allocateLinks() {
375 int index, lcount = 0;
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700376 linkTasks.clear();
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700377 for (LinkDescription ld : linkDescrs) {
378 index = (lcount % THREADS);
379 log.info("allocation: total={}, index={}", linkDescrs.size(), lcount, index);
380 if (linkTasks.size() <= index) {
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700381 linkTasks.add(index, Lists.newArrayList(ld));
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700382 } else {
383 linkTasks.get(index).add(ld);
384 }
385 lcount++;
386 }
Ayaka Koshibed2c7ad22015-02-13 16:44:07 -0800387 }
388
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800389 /**
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800390 * Generate LinkEvents using configurations when devices are found.
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800391 */
392 private class InternalLinkProvider implements DeviceListener {
393
394 @Override
395 public void event(DeviceEvent event) {
Ayaka Koshibe0cd42822015-01-22 16:02:31 -0800396 Device dev = event.subject();
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800397 switch (event.type()) {
398 case DEVICE_ADDED:
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700399 // TODO: wait for all devices to stop core from balking
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800400 break;
401 case DEVICE_REMOVED:
Ayaka Koshibe97940562015-03-11 12:25:46 -0700402 for (LinkDriver d : driverMap.get(dev.id())) {
403 d.deviceRemoved(dev.id());
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800404 }
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800405 providerService.linksVanished(dev.id());
Ayaka Koshibe35c71e12015-01-27 17:10:04 -0800406 break;
407 default:
408 break;
409 }
410 }
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800411 }
412
413 /**
414 * Generates link events using fake links.
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700415 * TODO: stats collection should be its own thing.
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800416 */
417 private class LinkDriver implements Runnable {
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700418 // List to actually work off of
Ayaka Koshibe97940562015-03-11 12:25:46 -0700419 List<LinkDescription> tasks = Lists.newCopyOnWriteArrayList();
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700420 float effLoad = 0;
421 Long counter = 0L;
422 int next = 0;
Ayaka Koshibe97940562015-03-11 12:25:46 -0700423 boolean up = true;
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700424
425 long startTime;
426
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700427 LinkDriver(List<LinkDescription> links) {
428 setTasks(links);
429 startTime = System.currentTimeMillis(); // yes, this will start off inaccurate
Ayaka Koshibed2c7ad22015-02-13 16:44:07 -0800430 }
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800431
432 @Override
433 public void run() {
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800434 if (flicker) {
435 flicker();
436 } else {
437 refresh();
438 }
439 }
440
441 private void flicker() {
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700442 if ((!linkDriver.isShutdown() || !tasks.isEmpty())) {
Ayaka Koshibe97940562015-03-11 12:25:46 -0700443 log.trace("next: {}, count: {}", next, counter);
444 if (counter <= CHECK_DURATION * 1_000_000 / eventRate) {
445 if (up) {
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700446 providerService.linkDetected(tasks.get(next++));
Ayaka Koshibe97940562015-03-11 12:25:46 -0700447 } else {
448 providerService.linkVanished(tasks.get(next++));
suibin1a7b7bd2015-02-12 09:41:47 -0800449 }
Ayaka Koshibe97940562015-03-11 12:25:46 -0700450 if (next >= tasks.size()) {
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700451 next = 0;
Ayaka Koshibe97940562015-03-11 12:25:46 -0700452 up = !up;
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700453 }
454 counter++;
suibin1a7b7bd2015-02-12 09:41:47 -0800455 } else {
456 // log in WARN the effective load generation rate in events/sec, every 10 seconds
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700457 effLoad = (float) (counter * 1000.0 / (System
458 .currentTimeMillis() - startTime));
Ayaka Koshibed2c7ad22015-02-13 16:44:07 -0800459 log.warn("Effective Loading for thread is {} events/second",
460 String.valueOf(effLoad));
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700461 counter = 0L;
suibin1a7b7bd2015-02-12 09:41:47 -0800462 startTime = System.currentTimeMillis();
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800463 }
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700464 linkDriver.schedule(this, eventRate, TimeUnit.MICROSECONDS);
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800465 }
466 }
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800467
468 private void refresh() {
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700469 if (!linkDriver.isShutdown() || !tasks.isEmpty()) {
Ayaka Koshibe97940562015-03-11 12:25:46 -0700470 log.trace("iter {} refresh_links", counter);
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700471
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700472 for (LinkDescription desc : tasks) {
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800473 providerService.linkDetected(desc);
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700474 log.info("iteration {}, {}", counter, desc);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800475 }
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700476 counter++;
477 linkDriver.schedule(this, REFRESH_RATE, TimeUnit.SECONDS);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800478 }
479 }
480
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700481 public void deviceRemoved(DeviceId did) {
Ayaka Koshibe97940562015-03-11 12:25:46 -0700482 List<LinkDescription> rm = Lists.newArrayList();
483 for (LinkDescription ld : tasks) {
484 if (did.equals(ld.dst().deviceId())
485 || (did.equals(ld.src().deviceId()))) {
486 rm.add(ld);
Ayaka Koshibe9209ea22015-03-09 10:57:50 -0700487 }
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800488 }
Ayaka Koshibe97940562015-03-11 12:25:46 -0700489 tasks.removeAll(rm);
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800490 }
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700491
492 public void setTasks(List<LinkDescription> links) {
493 HashMultimap<ConnectPoint, ConnectPoint> nm = HashMultimap.create();
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700494 List<LinkDescription> rm = Lists.newArrayList();
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700495 links.forEach(v -> nm.put(v.src(), v.dst()));
496 // remove and send linkVanished for stale links.
Ayaka Koshibe97940562015-03-11 12:25:46 -0700497 for (LinkDescription l : tasks) {
498 if (!nm.containsEntry(l.src(), l.dst())) {
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700499 rm.add(l);
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700500 }
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700501 }
Ayaka Koshibe97940562015-03-11 12:25:46 -0700502 tasks.clear();
503 tasks.addAll(links);
Ayaka Koshibeef69be32015-03-15 22:29:19 -0700504 rm.forEach(l -> providerService.linkVanished(l));
Ayaka Koshibe13f896f2015-03-10 15:01:44 -0700505 }
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800506 }
Ayaka Koshibe839a8a92015-03-03 17:07:22 -0800507
Ayaka Koshibe422916f2015-01-15 15:30:23 -0800508}