blob: fd70a0b59cc3ca442e71a1a92a538a4fde7471d3 [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) {
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800269 log.warn("A batch is stuck processing. " +
270 "pending : {}",
alshabib486349d2014-11-25 18:09:25 -0500271 intentBatchService.getPendingOperations());
272 shutdownAndAwaitTermination(randomWorker);
273 }
274 }
275 //if everyting is good proceed.
276 if (!randomWorker.isShutdown()) {
277 randomWorker.execute(this);
278 }
279
280 }
281
282 public void shutdown() {
283 log.warn("Shutting down random installer!");
284 cleanUp();
285 }
286
287
288 /**
289 * Shuffle the uninstalled and installed list (separately) and select
290 * a random number of them and install or uninstall them respectively.
291 */
292 private void randomize() {
293 List<HostPair> hostList = new LinkedList<>(uninstalledOrWithdrawn);
294 Collections.shuffle(hostList);
295 List<HostPair> toInstall = hostList.subList(0,
296 random.nextInt(hostList.size() - 1));
297 List<HostPair> toRemove;
298 if (!installed.isEmpty()) {
299 hostList = new LinkedList<>(installed);
300 Collections.shuffle(hostList);
301 toRemove = hostList.subList(0,
302 random.nextInt(hostList.size() - 1));
303 uninstallIntents(toRemove);
304 }
305 installIntents(toInstall);
306
307 }
308
309 private void installIntents(List<HostPair> toInstall) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800310 IntentOperations.Builder builder = IntentOperations.builder(appId);
alshabib486349d2014-11-25 18:09:25 -0500311 for (HostPair pair : toInstall) {
312 installed.add(pair);
313 uninstalledOrWithdrawn.remove(pair);
314 builder.addSubmitOperation(pair.h2hIntent());
315 }
316 intentBatchService.addIntentOperations(builder.build());
317 }
318
319 private void uninstallIntents(Collection<HostPair> toRemove) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800320 IntentOperations.Builder builder = IntentOperations.builder(appId);
alshabib486349d2014-11-25 18:09:25 -0500321 for (HostPair pair : toRemove) {
322 installed.remove(pair);
323 uninstalledOrWithdrawn.add(pair);
324 builder.addWithdrawOperation(pair.h2hIntent().id());
325 }
326 intentBatchService.addIntentOperations(builder.build());
327 }
328
329 /**
330 * Take everything and remove it all.
331 */
332 private void cleanUp() {
333 List<HostPair> allPairs = Lists.newArrayList(installed);
334 allPairs.addAll(uninstalledOrWithdrawn);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800335 IntentOperations.Builder builder = IntentOperations.builder(appId);
alshabib486349d2014-11-25 18:09:25 -0500336 for (HostPair pair : allPairs) {
337 builder.addWithdrawOperation(pair.h2hIntent().id());
338 }
339 intentBatchService.addIntentOperations(builder.build());
340 }
341
342
343 private Set<HostPair> buildPairs(Set<Host> hosts) {
344 Set<HostPair> pairs = Sets.newHashSet();
345 Iterator<Host> it = Sets.newHashSet(hosts).iterator();
346 while (it.hasNext()) {
347 Host src = it.next();
348 it.remove();
349 for (Host dst : hosts) {
350 pairs.add(new HostPair(src, dst));
351 }
352 }
353 return pairs;
354 }
355
356 private Set<Host> pruneHostsByMasterShip() {
357 return FluentIterable.from(hosts)
358 .filter(hasLocalMaster())
359 .toSet();
360
361 }
362
363 private Predicate<? super Host> hasLocalMaster() {
364 return new Predicate<Host>() {
365 @Override
366 public boolean apply(Host host) {
367 return mastershipService.getLocalRole(
368 host.location().deviceId()).equals(MastershipRole.MASTER);
369 }
370 };
371 }
372
373
374 /**
375 * Simple class representing a pair of hosts and precomputes the associated
376 * h2h intent.
377 */
378 private class HostPair {
379
380 private final Host src;
381 private final Host dst;
382
383 private final TrafficSelector selector = DefaultTrafficSelector.builder().build();
384 private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
385 private final List<Constraint> constraint = Lists.newArrayList();
386 private final HostToHostIntent intent;
387
388 public HostPair(Host src, Host dst) {
389 this.src = src;
390 this.dst = dst;
391 this.intent = new HostToHostIntent(appId, src.id(), dst.id(),
392 selector, treatment, constraint);
393 }
394
395 public HostToHostIntent h2hIntent() {
396 return intent;
397 }
398
399 @Override
400 public boolean equals(Object o) {
401 if (this == o) {
402 return true;
403 }
404 if (o == null || getClass() != o.getClass()) {
405 return false;
406 }
407
408 HostPair hostPair = (HostPair) o;
409
410 return Objects.equals(src, hostPair.src) &&
411 Objects.equals(dst, hostPair.dst);
412
413 }
414
415 @Override
416 public int hashCode() {
417 return Objects.hash(src, dst);
418 }
419
420
421 }
422
423 }
424
425 /**
426 * Remove anything that is running and clear it all out.
427 */
alshabibfd23d312014-11-11 18:14:47 -0800428 private class UnInstaller implements Runnable {
429 @Override
430 public void run() {
alshabib486349d2014-11-25 18:09:25 -0500431 if (!existingIntents.isEmpty()) {
432 clearExistingIntents();
433 }
434
435 if (randomWorker != null && !randomWorker.isShutdown()) {
436 shutdownAndAwaitTermination(randomWorker);
437 randomInstaller.shutdown();
438 }
439 }
440
441 private void clearExistingIntents() {
alshabibfd23d312014-11-11 18:14:47 -0800442 for (Intent i : existingIntents) {
443 intentService.withdraw(i);
444 }
alshabib486349d2014-11-25 18:09:25 -0500445 existingIntents.clear();
alshabibfd23d312014-11-11 18:14:47 -0800446 }
447 }
alshabib486349d2014-11-25 18:09:25 -0500448
449 /**
450 * Shutdown a pool cleanly if possible.
451 *
452 * @param pool an executorService
453 */
454 private void shutdownAndAwaitTermination(ExecutorService pool) {
455 pool.shutdown(); // Disable new tasks from being submitted
456 try {
457 // Wait a while for existing tasks to terminate
458 if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
459 pool.shutdownNow(); // Cancel currently executing tasks
460 // Wait a while for tasks to respond to being cancelled
461 if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
462 log.error("Pool did not terminate");
463 }
464 }
465 } catch (Exception ie) {
466 // (Re-)Cancel if current thread also interrupted
467 pool.shutdownNow();
468 // Preserve interrupt status
469 Thread.currentThread().interrupt();
470 }
471 }
472
alshabibfd23d312014-11-11 18:14:47 -0800473}
474
475