blob: 74bfa123587132c459522231506c1f0394fbd39f [file] [log] [blame]
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuskac40d4632015-04-09 16:55:03 -07003 *
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;
17
18import com.google.common.collect.Lists;
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080019import com.google.common.collect.Maps;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070020import org.onosproject.net.ConnectPoint;
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080021import org.onosproject.net.DeviceId;
22import org.onosproject.net.Link;
23import org.onosproject.net.device.DeviceProviderService;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070024import org.onosproject.net.device.DeviceService;
25import org.onosproject.net.link.DefaultLinkDescription;
26import org.onosproject.net.link.LinkDescription;
27import org.onosproject.net.link.LinkProviderService;
28import org.onosproject.net.link.LinkService;
29import org.slf4j.Logger;
30import org.slf4j.LoggerFactory;
31
32import java.util.List;
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080033import java.util.Map;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070034import java.util.Random;
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080035import java.util.Set;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070036import java.util.concurrent.ExecutorService;
37import java.util.stream.Collectors;
38
39import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
40import static org.onlab.util.Tools.delay;
41import static org.onlab.util.Tools.groupedThreads;
42import static org.onosproject.net.Link.Type.DIRECT;
43import static org.onosproject.net.MastershipRole.MASTER;
44import static org.onosproject.provider.nil.TopologySimulator.description;
45
46/**
47 * Drives topology mutations at a specified rate of events per second.
48 */
49class TopologyMutationDriver implements Runnable {
50
51 private final Logger log = LoggerFactory.getLogger(getClass());
52
53 private static final int WAIT_DELAY = 2_000;
54 private static final int MAX_DOWN_LINKS = 5;
55
56 private final Random random = new Random();
57
58 private volatile boolean stopped = true;
59
60 private double mutationRate;
61 private int millis, nanos;
62
63 private LinkService linkService;
64 private DeviceService deviceService;
65 private LinkProviderService linkProviderService;
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080066 private DeviceProviderService deviceProviderService;
67 private TopologySimulator simulator;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070068
69 private List<LinkDescription> activeLinks;
70 private List<LinkDescription> inactiveLinks;
71
72 private final ExecutorService executor =
Andrea Campanella90f044f2016-03-02 09:14:57 -080073 newSingleThreadScheduledExecutor(groupedThreads("onos/null", "topo-mutator", log));
Thomas Vachuskac40d4632015-04-09 16:55:03 -070074
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080075 private Map<DeviceId, Set<Link>> savedLinks = Maps.newConcurrentMap();
76
Thomas Vachuskac40d4632015-04-09 16:55:03 -070077 /**
78 * Starts the mutation process.
79 *
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080080 * @param mutationRate link events per second
81 * @param linkService link service
82 * @param deviceService device service
83 * @param linkProviderService link provider service
84 * @param deviceProviderService device provider service
85 * @param simulator topology simulator
Thomas Vachuskac40d4632015-04-09 16:55:03 -070086 */
87 void start(double mutationRate,
88 LinkService linkService, DeviceService deviceService,
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080089 LinkProviderService linkProviderService,
90 DeviceProviderService deviceProviderService,
91 TopologySimulator simulator) {
92 savedLinks.clear();
Thomas Vachuskac40d4632015-04-09 16:55:03 -070093 stopped = false;
94 this.linkService = linkService;
95 this.deviceService = deviceService;
96 this.linkProviderService = linkProviderService;
Thomas Vachuska8e038eb2016-02-23 23:28:23 -080097 this.deviceProviderService = deviceProviderService;
98 this.simulator = simulator;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070099 activeLinks = reduceLinks();
100 inactiveLinks = Lists.newArrayList();
101 adjustRate(mutationRate);
Andrea Campanella90f044f2016-03-02 09:14:57 -0800102 executor.execute(this);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700103 }
104
105 /**
106 * Adjusts the topology mutation rate.
107 *
108 * @param mutationRate new topology mutation rate
109 */
110 void adjustRate(double mutationRate) {
111 this.mutationRate = mutationRate;
112 if (mutationRate > 0) {
113 this.millis = (int) (1_000 / mutationRate / 2);
114 this.nanos = (int) (1_000_000 / mutationRate / 2) % 1_000_000;
115 } else {
116 this.millis = 0;
117 this.nanos = 0;
118 }
119 log.info("Settings: millis={}, nanos={}", millis, nanos);
120 }
121
122 /**
123 * Stops the mutation process.
124 */
125 void stop() {
126 stopped = true;
127 }
128
129 /**
130 * Severs the link between the specified end-points in both directions.
131 *
132 * @param one link endpoint
133 * @param two link endpoint
134 */
135 void severLink(ConnectPoint one, ConnectPoint two) {
136 LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
137 linkProviderService.linkVanished(link);
138 linkProviderService.linkVanished(reverse(link));
139
140 }
141
142 /**
143 * Repairs the link between the specified end-points in both directions.
144 *
145 * @param one link endpoint
146 * @param two link endpoint
147 */
148 void repairLink(ConnectPoint one, ConnectPoint two) {
149 LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
150 linkProviderService.linkDetected(link);
151 linkProviderService.linkDetected(reverse(link));
152 }
153
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800154 /**
155 * Fails the specified device.
156 *
157 * @param deviceId device identifier
158 */
159 void failDevice(DeviceId deviceId) {
160 savedLinks.put(deviceId, linkService.getDeviceLinks(deviceId));
161 deviceProviderService.deviceDisconnected(deviceId);
162 }
163
164 /**
165 * Repairs the specified device.
166 *
167 * @param deviceId device identifier
168 */
169 void repairDevice(DeviceId deviceId) {
Simon Hunta77d3bf2017-01-30 15:27:36 -0800170 // device IDs are expressed in hexadecimal... (use radix 16)
171 int chassisId = Integer.parseInt(deviceId.uri().getSchemeSpecificPart(), 16);
Thomas Vachuska8e038eb2016-02-23 23:28:23 -0800172 simulator.createDevice(deviceId, chassisId);
173 Set<Link> links = savedLinks.remove(deviceId);
174 if (links != null) {
175 links.forEach(l -> linkProviderService
176 .linkDetected(new DefaultLinkDescription(l.src(), l.dst(), DIRECT)));
177 }
178 }
179
180 /**
181 * Returns whether the given device is considered reachable or not.
182 *
183 * @param deviceId device identifier
184 * @return true if device is reachable
185 */
186 boolean isReachable(DeviceId deviceId) {
187 return !savedLinks.containsKey(deviceId);
188 }
189
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700190 @Override
191 public void run() {
192 delay(WAIT_DELAY);
193
194 while (!stopped) {
195 if (mutationRate > 0 && inactiveLinks.isEmpty()) {
196 primeInactiveLinks();
197 } else if (mutationRate <= 0 && !inactiveLinks.isEmpty()) {
198 repairInactiveLinks();
199 } else if (inactiveLinks.isEmpty()) {
200 delay(WAIT_DELAY);
201
202 } else {
203 activeLinks.add(repairLink());
204 pause();
205 inactiveLinks.add(severLink());
206 pause();
207 }
208 }
209 }
210
211 // Primes the inactive links with a few random links.
212 private void primeInactiveLinks() {
213 for (int i = 0, n = Math.min(MAX_DOWN_LINKS, activeLinks.size()); i < n; i++) {
214 inactiveLinks.add(severLink());
215 }
216 }
217
218 // Repairs all inactive links.
219 private void repairInactiveLinks() {
220 while (!inactiveLinks.isEmpty()) {
221 repairLink();
222 }
223 }
224
225 // Picks a random active link and severs it.
226 private LinkDescription severLink() {
227 LinkDescription link = getRandomLink(activeLinks);
228 linkProviderService.linkVanished(link);
229 linkProviderService.linkVanished(reverse(link));
230 return link;
231 }
232
233 // Picks a random inactive link and repairs it.
234 private LinkDescription repairLink() {
235 LinkDescription link = getRandomLink(inactiveLinks);
236 linkProviderService.linkDetected(link);
237 linkProviderService.linkDetected(reverse(link));
238 return link;
239 }
240
241 // Produces a reverse of the specified link.
242 private LinkDescription reverse(LinkDescription link) {
243 return new DefaultLinkDescription(link.dst(), link.src(), link.type());
244 }
245
246 // Returns a random link from the specified list of links.
247 private LinkDescription getRandomLink(List<LinkDescription> links) {
248 return links.remove(random.nextInt(links.size()));
249 }
250
251 // Reduces the given list of links to just a single link in each original pair.
252 private List<LinkDescription> reduceLinks() {
253 List<LinkDescription> links = Lists.newArrayList();
254 linkService.getLinks().forEach(link -> links.add(description(link)));
255 return links.stream()
256 .filter(this::isOurLink)
257 .filter(this::isRightDirection)
258 .collect(Collectors.toList());
259 }
260
261 // Returns true if the specified link is ours.
262 private boolean isOurLink(LinkDescription linkDescription) {
263 return deviceService.getRole(linkDescription.src().deviceId()) == MASTER;
264 }
265
266 // Returns true if the link source is greater than the link destination.
267 private boolean isRightDirection(LinkDescription link) {
268 return link.src().deviceId().toString().compareTo(link.dst().deviceId().toString()) > 0;
269 }
270
271 // Pauses the current thread for the pre-computed time of millis & nanos.
272 private void pause() {
273 delay(millis, nanos);
274 }
275
276}