blob: de66ee52d5cd6d25d7d0a73c14d439d8a89853d9 [file] [log] [blame]
alshabibfd23d312014-11-11 18:14:47 -08001/*
2 * Copyright 2014 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.onlab.onos.demo;
17
alshabib486349d2014-11-25 18:09:25 -050018import com.fasterxml.jackson.databind.JsonNode;
19import com.google.common.base.Predicate;
20import com.google.common.collect.FluentIterable;
alshabibfd23d312014-11-11 18:14:47 -080021import com.google.common.collect.Lists;
alshabib486349d2014-11-25 18:09:25 -050022import com.google.common.collect.Sets;
alshabibfd23d312014-11-11 18:14:47 -080023import com.google.common.util.concurrent.ThreadFactoryBuilder;
24import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.apache.felix.scr.annotations.Service;
alshabib486349d2014-11-25 18:09:25 -050030
31import org.onlab.onos.cluster.ClusterService;
alshabibfd23d312014-11-11 18:14:47 -080032import org.onlab.onos.core.ApplicationId;
33import org.onlab.onos.core.CoreService;
alshabib486349d2014-11-25 18:09:25 -050034import org.onlab.onos.mastership.MastershipService;
alshabibfd23d312014-11-11 18:14:47 -080035import org.onlab.onos.net.Host;
alshabib486349d2014-11-25 18:09:25 -050036import org.onlab.onos.net.HostId;
37import org.onlab.onos.net.MastershipRole;
alshabibfd23d312014-11-11 18:14:47 -080038import org.onlab.onos.net.flow.DefaultTrafficSelector;
39import org.onlab.onos.net.flow.DefaultTrafficTreatment;
40import org.onlab.onos.net.flow.TrafficSelector;
41import org.onlab.onos.net.flow.TrafficTreatment;
42import org.onlab.onos.net.host.HostService;
alshabib19678cc2014-11-12 11:06:08 -080043import org.onlab.onos.net.intent.Constraint;
alshabibfd23d312014-11-11 18:14:47 -080044import org.onlab.onos.net.intent.HostToHostIntent;
45import org.onlab.onos.net.intent.Intent;
alshabib486349d2014-11-25 18:09:25 -050046import org.onlab.onos.net.intent.IntentBatchService;
47import org.onlab.onos.net.intent.IntentOperations;
alshabibfd23d312014-11-11 18:14:47 -080048import org.onlab.onos.net.intent.IntentService;
49import org.slf4j.Logger;
50
51
alshabib486349d2014-11-25 18:09:25 -050052import java.util.Collection;
53import java.util.Collections;
alshabibfd23d312014-11-11 18:14:47 -080054import java.util.HashSet;
alshabib486349d2014-11-25 18:09:25 -050055import java.util.Iterator;
56import java.util.LinkedList;
alshabibfd23d312014-11-11 18:14:47 -080057import java.util.List;
alshabib486349d2014-11-25 18:09:25 -050058import java.util.Objects;
59import java.util.Optional;
60import java.util.Random;
alshabibfd23d312014-11-11 18:14:47 -080061import java.util.Set;
alshabib486349d2014-11-25 18:09:25 -050062import java.util.concurrent.CountDownLatch;
alshabibfd23d312014-11-11 18:14:47 -080063import java.util.concurrent.ExecutorService;
64import java.util.concurrent.Executors;
alshabib486349d2014-11-25 18:09:25 -050065import java.util.concurrent.TimeUnit;
66
alshabibfd23d312014-11-11 18:14:47 -080067
68import static org.slf4j.LoggerFactory.getLogger;
69
70/**
71 * Application to set up demos.
72 */
73@Component(immediate = true)
74@Service
75public class DemoInstaller implements DemoAPI {
76
77 private final Logger log = getLogger(getClass());
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CoreService coreService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected IntentService intentService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected HostService hostService;
87
alshabib486349d2014-11-25 18:09:25 -050088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected MastershipService mastershipService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected IntentBatchService intentBatchService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected ClusterService clusterService;
96
alshabibfd23d312014-11-11 18:14:47 -080097 private ExecutorService worker;
98
alshabib486349d2014-11-25 18:09:25 -050099 private ExecutorService randomWorker;
100
alshabibfd23d312014-11-11 18:14:47 -0800101 private ApplicationId appId;
102
103 private final Set<Intent> existingIntents = new HashSet<>();
alshabib486349d2014-11-25 18:09:25 -0500104 private RandomInstaller randomInstaller;
alshabibfd23d312014-11-11 18:14:47 -0800105
106
107
108 @Activate
109 public void activate() {
alshabib486349d2014-11-25 18:09:25 -0500110 String nodeId = clusterService.getLocalNode().ip().toString();
111 appId = coreService.registerApplication("org.onlab.onos.demo.installer."
112 + nodeId);
alshabibfd23d312014-11-11 18:14:47 -0800113 worker = Executors.newFixedThreadPool(1,
114 new ThreadFactoryBuilder()
115 .setNameFormat("demo-app-worker")
116 .build());
117 log.info("Started with Application ID {}", appId.id());
118 }
119
120 @Deactivate
121 public void deactivate() {
alshabib486349d2014-11-25 18:09:25 -0500122 shutdownAndAwaitTermination(worker);
123 if (!randomWorker.isShutdown()) {
124 shutdownAndAwaitTermination(randomWorker);
125 }
alshabibfd23d312014-11-11 18:14:47 -0800126 log.info("Stopped");
127 }
128
129 @Override
alshabib486349d2014-11-25 18:09:25 -0500130 public void setup(InstallType type, Optional<JsonNode> runParams) {
alshabibfd23d312014-11-11 18:14:47 -0800131 switch (type) {
132 case MESH:
133 log.debug("Installing mesh intents");
134 worker.execute(new MeshInstaller());
135 break;
136 case RANDOM:
alshabib486349d2014-11-25 18:09:25 -0500137 //check that we do not have a random installer running
138 if (randomWorker == null || randomWorker.isShutdown()) {
139 randomWorker = Executors.newFixedThreadPool(1,
140 new ThreadFactoryBuilder()
141 .setNameFormat("random-worker")
142 .build());
143 log.debug("Installing random sequence of intents");
144 randomInstaller = new RandomInstaller(runParams);
145 randomWorker.execute(randomInstaller);
146 } else {
147 log.warn("Random installer is already running");
148 }
149 break;
alshabibfd23d312014-11-11 18:14:47 -0800150 default:
151 throw new IllegalArgumentException("What is it you want exactly?");
152 }
153 }
154
155 @Override
156 public void tearDown() {
157 worker.submit(new UnInstaller());
158 }
159
160
alshabib486349d2014-11-25 18:09:25 -0500161 /**
162 * Simply installs a mesh of intents from all the hosts existing in the network.
163 */
alshabibfd23d312014-11-11 18:14:47 -0800164 private class MeshInstaller implements Runnable {
165
166 @Override
167 public void run() {
168 TrafficSelector selector = DefaultTrafficSelector.builder().build();
169 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
alshabib19678cc2014-11-12 11:06:08 -0800170 List<Constraint> constraint = Lists.newArrayList();
alshabibfd23d312014-11-11 18:14:47 -0800171 List<Host> hosts = Lists.newArrayList(hostService.getHosts());
172 while (!hosts.isEmpty()) {
173 Host src = hosts.remove(0);
174 for (Host dst : hosts) {
175 HostToHostIntent intent = new HostToHostIntent(appId, src.id(), dst.id(),
176 selector, treatment,
alshabib19678cc2014-11-12 11:06:08 -0800177 constraint);
alshabibfd23d312014-11-11 18:14:47 -0800178 existingIntents.add(intent);
179 intentService.submit(intent);
180 }
181 }
182 }
183 }
184
alshabib486349d2014-11-25 18:09:25 -0500185 /**
186 * Randomly installs and withdraws intents.
187 */
188 private class RandomInstaller implements Runnable {
alshabibfd23d312014-11-11 18:14:47 -0800189
alshabib486349d2014-11-25 18:09:25 -0500190 private final boolean isLocal;
191 private final Set<Host> hosts;
192
193 private final Random random = new Random(System.currentTimeMillis());
194
195 private Set<HostPair> uninstalledOrWithdrawn;
196 private Set<HostPair> installed;
197
198 private CountDownLatch latch;
199
200 //used to wait on a batch to be processed.
201 private static final int ITERATIONMAX = 50000000;
202
203
204 public RandomInstaller(Optional<JsonNode> runParams) {
205 /*
206 Check if we have params and honour them. Otherwise
207 set defaults to processing only local stuff and
208 all local hosts.
209 */
210 if (runParams.isPresent()) {
211 JsonNode node = runParams.get();
212 isLocal = node.get("local").asBoolean();
213 hosts = node.get("hosts") == null ? Sets.newHashSet(hostService.getHosts()) :
214 constructHostIds(node.get("hosts").elements());
215 } else {
216 isLocal = true;
217 hosts = Sets.newHashSet(hostService.getHosts());
218 }
219
220 //construct list of intents.
221 installed = Sets.newHashSet();
222 if (isLocal) {
223 uninstalledOrWithdrawn = buildPairs(pruneHostsByMasterShip());
224 } else {
225 uninstalledOrWithdrawn = buildPairs(hosts);
226 }
227
228 }
229
230 private Set<Host> constructHostIds(Iterator<JsonNode> elements) {
231 Set<Host> hostIds = Sets.newHashSet();
232 JsonNode n;
233 while (elements.hasNext()) {
234 n = elements.next();
235 hostIds.add(hostService.getHost(HostId.hostId(n.textValue())));
236 }
237 return hostIds;
238 }
239
240 @Override
241 public void run() {
242 if (!randomWorker.isShutdown()) {
243 randomize();
244 latch = new CountDownLatch(1);
245 try {
246 trackIntents();
247 } catch (InterruptedException e) {
248 shutdown();
249 }
250 }
251
252 }
253
254
255 /**
256 * Check whether the previously submitted batch is in progress
257 * and if yes submit the next one. If things hang, wait for at
258 * most 5 seconds and bail.
259 * @throws InterruptedException if the thread go interupted
260 */
261 private void trackIntents() throws InterruptedException {
262 int count = 0;
263 while (!latch.await(100, TimeUnit.NANOSECONDS)) {
264 if (intentBatchService.getPendingOperations().isEmpty()) {
265 latch.countDown();
266 }
267 count++;
268 if (count > ITERATIONMAX) {
269 log.warn("A batch is stuck processing. current : {}" +
270 ", pending : {}",
271 intentBatchService.getCurrentOperations(),
272 intentBatchService.getPendingOperations());
273 shutdownAndAwaitTermination(randomWorker);
274 }
275 }
276 //if everyting is good proceed.
277 if (!randomWorker.isShutdown()) {
278 randomWorker.execute(this);
279 }
280
281 }
282
283 public void shutdown() {
284 log.warn("Shutting down random installer!");
285 cleanUp();
286 }
287
288
289 /**
290 * Shuffle the uninstalled and installed list (separately) and select
291 * a random number of them and install or uninstall them respectively.
292 */
293 private void randomize() {
294 List<HostPair> hostList = new LinkedList<>(uninstalledOrWithdrawn);
295 Collections.shuffle(hostList);
296 List<HostPair> toInstall = hostList.subList(0,
297 random.nextInt(hostList.size() - 1));
298 List<HostPair> toRemove;
299 if (!installed.isEmpty()) {
300 hostList = new LinkedList<>(installed);
301 Collections.shuffle(hostList);
302 toRemove = hostList.subList(0,
303 random.nextInt(hostList.size() - 1));
304 uninstallIntents(toRemove);
305 }
306 installIntents(toInstall);
307
308 }
309
310 private void installIntents(List<HostPair> toInstall) {
311 IntentOperations.Builder builder = IntentOperations.builder();
312 for (HostPair pair : toInstall) {
313 installed.add(pair);
314 uninstalledOrWithdrawn.remove(pair);
315 builder.addSubmitOperation(pair.h2hIntent());
316 }
317 intentBatchService.addIntentOperations(builder.build());
318 }
319
320 private void uninstallIntents(Collection<HostPair> toRemove) {
321 IntentOperations.Builder builder = IntentOperations.builder();
322 for (HostPair pair : toRemove) {
323 installed.remove(pair);
324 uninstalledOrWithdrawn.add(pair);
325 builder.addWithdrawOperation(pair.h2hIntent().id());
326 }
327 intentBatchService.addIntentOperations(builder.build());
328 }
329
330 /**
331 * Take everything and remove it all.
332 */
333 private void cleanUp() {
334 List<HostPair> allPairs = Lists.newArrayList(installed);
335 allPairs.addAll(uninstalledOrWithdrawn);
336 IntentOperations.Builder builder = IntentOperations.builder();
337 for (HostPair pair : allPairs) {
338 builder.addWithdrawOperation(pair.h2hIntent().id());
339 }
340 intentBatchService.addIntentOperations(builder.build());
341 }
342
343
344 private Set<HostPair> buildPairs(Set<Host> hosts) {
345 Set<HostPair> pairs = Sets.newHashSet();
346 Iterator<Host> it = Sets.newHashSet(hosts).iterator();
347 while (it.hasNext()) {
348 Host src = it.next();
349 it.remove();
350 for (Host dst : hosts) {
351 pairs.add(new HostPair(src, dst));
352 }
353 }
354 return pairs;
355 }
356
357 private Set<Host> pruneHostsByMasterShip() {
358 return FluentIterable.from(hosts)
359 .filter(hasLocalMaster())
360 .toSet();
361
362 }
363
364 private Predicate<? super Host> hasLocalMaster() {
365 return new Predicate<Host>() {
366 @Override
367 public boolean apply(Host host) {
368 return mastershipService.getLocalRole(
369 host.location().deviceId()).equals(MastershipRole.MASTER);
370 }
371 };
372 }
373
374
375 /**
376 * Simple class representing a pair of hosts and precomputes the associated
377 * h2h intent.
378 */
379 private class HostPair {
380
381 private final Host src;
382 private final Host dst;
383
384 private final TrafficSelector selector = DefaultTrafficSelector.builder().build();
385 private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
386 private final List<Constraint> constraint = Lists.newArrayList();
387 private final HostToHostIntent intent;
388
389 public HostPair(Host src, Host dst) {
390 this.src = src;
391 this.dst = dst;
392 this.intent = new HostToHostIntent(appId, src.id(), dst.id(),
393 selector, treatment, constraint);
394 }
395
396 public HostToHostIntent h2hIntent() {
397 return intent;
398 }
399
400 @Override
401 public boolean equals(Object o) {
402 if (this == o) {
403 return true;
404 }
405 if (o == null || getClass() != o.getClass()) {
406 return false;
407 }
408
409 HostPair hostPair = (HostPair) o;
410
411 return Objects.equals(src, hostPair.src) &&
412 Objects.equals(dst, hostPair.dst);
413
414 }
415
416 @Override
417 public int hashCode() {
418 return Objects.hash(src, dst);
419 }
420
421
422 }
423
424 }
425
426 /**
427 * Remove anything that is running and clear it all out.
428 */
alshabibfd23d312014-11-11 18:14:47 -0800429 private class UnInstaller implements Runnable {
430 @Override
431 public void run() {
alshabib486349d2014-11-25 18:09:25 -0500432 if (!existingIntents.isEmpty()) {
433 clearExistingIntents();
434 }
435
436 if (randomWorker != null && !randomWorker.isShutdown()) {
437 shutdownAndAwaitTermination(randomWorker);
438 randomInstaller.shutdown();
439 }
440 }
441
442 private void clearExistingIntents() {
alshabibfd23d312014-11-11 18:14:47 -0800443 for (Intent i : existingIntents) {
444 intentService.withdraw(i);
445 }
alshabib486349d2014-11-25 18:09:25 -0500446 existingIntents.clear();
alshabibfd23d312014-11-11 18:14:47 -0800447 }
448 }
alshabib486349d2014-11-25 18:09:25 -0500449
450 /**
451 * Shutdown a pool cleanly if possible.
452 *
453 * @param pool an executorService
454 */
455 private void shutdownAndAwaitTermination(ExecutorService pool) {
456 pool.shutdown(); // Disable new tasks from being submitted
457 try {
458 // Wait a while for existing tasks to terminate
459 if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
460 pool.shutdownNow(); // Cancel currently executing tasks
461 // Wait a while for tasks to respond to being cancelled
462 if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
463 log.error("Pool did not terminate");
464 }
465 }
466 } catch (Exception ie) {
467 // (Re-)Cancel if current thread also interrupted
468 pool.shutdownNow();
469 // Preserve interrupt status
470 Thread.currentThread().interrupt();
471 }
472 }
473
alshabibfd23d312014-11-11 18:14:47 -0800474}
475
476