1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.jclouds.compute.internal;
20
21 import static com.google.common.base.Preconditions.checkNotNull;
22 import static com.google.common.base.Predicates.and;
23 import static com.google.common.base.Predicates.not;
24 import static com.google.common.base.Predicates.notNull;
25 import static com.google.common.collect.Iterables.concat;
26 import static com.google.common.collect.Iterables.filter;
27 import static com.google.common.collect.Maps.newLinkedHashMap;
28 import static com.google.common.collect.Sets.filter;
29 import static com.google.common.collect.Sets.newLinkedHashSet;
30 import static com.google.common.util.concurrent.Futures.immediateFuture;
31 import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
32 import static org.jclouds.compute.predicates.NodePredicates.all;
33 import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
34 import static org.jclouds.concurrent.FutureIterables.transformParallel;
35
36 import java.io.IOException;
37 import java.util.Map;
38 import java.util.NoSuchElementException;
39 import java.util.Set;
40 import java.util.concurrent.Callable;
41 import java.util.concurrent.ExecutorService;
42 import java.util.concurrent.Future;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.atomic.AtomicReference;
45
46 import javax.annotation.Resource;
47 import javax.inject.Inject;
48 import javax.inject.Named;
49 import javax.inject.Provider;
50 import javax.inject.Singleton;
51
52 import org.jclouds.Constants;
53 import org.jclouds.collect.Memoized;
54 import org.jclouds.compute.ComputeService;
55 import org.jclouds.compute.ComputeServiceContext;
56 import org.jclouds.compute.RunNodesException;
57 import org.jclouds.compute.RunScriptOnNodesException;
58 import org.jclouds.compute.callables.RunScriptOnNode;
59 import org.jclouds.compute.config.CustomizationResponse;
60 import org.jclouds.compute.domain.ComputeMetadata;
61 import org.jclouds.compute.domain.ExecResponse;
62 import org.jclouds.compute.domain.Hardware;
63 import org.jclouds.compute.domain.Image;
64 import org.jclouds.compute.domain.NodeMetadata;
65 import org.jclouds.compute.domain.NodeMetadataBuilder;
66 import org.jclouds.compute.domain.Template;
67 import org.jclouds.compute.domain.TemplateBuilder;
68 import org.jclouds.compute.options.RunScriptOptions;
69 import org.jclouds.compute.options.TemplateOptions;
70 import org.jclouds.compute.reference.ComputeServiceConstants;
71 import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
72 import org.jclouds.compute.strategy.DestroyNodeStrategy;
73 import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
74 import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
75 import org.jclouds.compute.strategy.ListNodesStrategy;
76 import org.jclouds.compute.strategy.RebootNodeStrategy;
77 import org.jclouds.compute.strategy.ResumeNodeStrategy;
78 import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
79 import org.jclouds.compute.strategy.RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
80 import org.jclouds.compute.strategy.SuspendNodeStrategy;
81 import org.jclouds.domain.Credentials;
82 import org.jclouds.domain.Location;
83 import org.jclouds.io.Payload;
84 import org.jclouds.logging.Logger;
85 import org.jclouds.predicates.RetryablePredicate;
86 import org.jclouds.scriptbuilder.domain.Statement;
87 import org.jclouds.scriptbuilder.domain.Statements;
88 import org.jclouds.util.Strings2;
89
90 import com.google.common.base.Function;
91 import com.google.common.base.Predicate;
92 import com.google.common.base.Supplier;
93 import com.google.common.base.Throwables;
94 import com.google.common.collect.ImmutableMap;
95 import com.google.common.collect.Iterables;
96 import com.google.common.collect.LinkedHashMultimap;
97 import com.google.common.collect.Multimap;
98
99
100
101
102
103 @Singleton
104 public class BaseComputeService implements ComputeService {
105
106 @Resource
107 @Named(ComputeServiceConstants.COMPUTE_LOGGER)
108 protected Logger logger = Logger.NULL;
109
110 protected final ComputeServiceContext context;
111 protected final Map<String, Credentials> credentialStore;
112
113 private final Supplier<Set<? extends Image>> images;
114 private final Supplier<Set<? extends Hardware>> hardwareProfiles;
115 private final Supplier<Set<? extends Location>> locations;
116 private final ListNodesStrategy listNodesStrategy;
117 private final GetNodeMetadataStrategy getNodeMetadataStrategy;
118 private final CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy;
119 private final RebootNodeStrategy rebootNodeStrategy;
120 private final DestroyNodeStrategy destroyNodeStrategy;
121 private final ResumeNodeStrategy resumeNodeStrategy;
122 private final SuspendNodeStrategy suspendNodeStrategy;
123 private final Provider<TemplateBuilder> templateBuilderProvider;
124 private final Provider<TemplateOptions> templateOptionsProvider;
125 private final Predicate<NodeMetadata> nodeRunning;
126 private final Predicate<NodeMetadata> nodeTerminated;
127 private final Predicate<NodeMetadata> nodeSuspended;
128 private final InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory;
129 private final Timeouts timeouts;
130 private final ExecutorService executor;
131
132 @Inject
133 protected BaseComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
134 @Memoized Supplier<Set<? extends Image>> images,
135 @Memoized Supplier<Set<? extends Hardware>> hardwareProfiles,
136 @Memoized Supplier<Set<? extends Location>> locations, ListNodesStrategy listNodesStrategy,
137 GetNodeMetadataStrategy getNodeMetadataStrategy, CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy,
138 RebootNodeStrategy rebootNodeStrategy, DestroyNodeStrategy destroyNodeStrategy,
139 ResumeNodeStrategy resumeNodeStrategy, SuspendNodeStrategy suspendNodeStrategy,
140 Provider<TemplateBuilder> templateBuilderProvider, Provider<TemplateOptions> templateOptionsProvider,
141 @Named("NODE_RUNNING") Predicate<NodeMetadata> nodeRunning,
142 @Named("NODE_TERMINATED") Predicate<NodeMetadata> nodeTerminated,
143 @Named("NODE_SUSPENDED") Predicate<NodeMetadata> nodeSuspended,
144 InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, Timeouts timeouts,
145 @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
146 this.context = checkNotNull(context, "context");
147 this.credentialStore = checkNotNull(credentialStore, "credentialStore");
148 this.images = checkNotNull(images, "images");
149 this.hardwareProfiles = checkNotNull(hardwareProfiles, "hardwareProfiles");
150 this.locations = checkNotNull(locations, "locations");
151 this.listNodesStrategy = checkNotNull(listNodesStrategy, "listNodesStrategy");
152 this.getNodeMetadataStrategy = checkNotNull(getNodeMetadataStrategy, "getNodeMetadataStrategy");
153 this.runNodesAndAddToSetStrategy = checkNotNull(runNodesAndAddToSetStrategy, "runNodesAndAddToSetStrategy");
154 this.rebootNodeStrategy = checkNotNull(rebootNodeStrategy, "rebootNodeStrategy");
155 this.resumeNodeStrategy = checkNotNull(resumeNodeStrategy, "resumeNodeStrategy");
156 this.suspendNodeStrategy = checkNotNull(suspendNodeStrategy, "suspendNodeStrategy");
157 this.destroyNodeStrategy = checkNotNull(destroyNodeStrategy, "destroyNodeStrategy");
158 this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider");
159 this.templateOptionsProvider = checkNotNull(templateOptionsProvider, "templateOptionsProvider");
160 this.nodeRunning = checkNotNull(nodeRunning, "nodeRunning");
161 this.nodeTerminated = checkNotNull(nodeTerminated, "nodeTerminated");
162 this.nodeSuspended = checkNotNull(nodeSuspended, "nodeSuspended");
163 this.initScriptRunnerFactory = checkNotNull(initScriptRunnerFactory, "initScriptRunnerFactory");
164 this.timeouts = checkNotNull(timeouts, "timeouts");
165 this.executor = checkNotNull(executor, "executor");
166 }
167
168
169
170
171 @Override
172 public ComputeServiceContext getContext() {
173 return context;
174 }
175
176
177
178
179 @Override
180 public Set<? extends NodeMetadata> runNodesWithTag(String group, int count, Template template)
181 throws RunNodesException {
182 return createNodesInGroup(group, count, template);
183 }
184
185
186
187
188 @Override
189 public Set<? extends NodeMetadata> runNodesWithTag(String group, int count, TemplateOptions templateOptions)
190 throws RunNodesException {
191 return createNodesInGroup(group, count, templateBuilder().any().options(templateOptions).build());
192 }
193
194
195
196
197 @Override
198 public Set<? extends NodeMetadata> runNodesWithTag(String group, int count) throws RunNodesException {
199 return createNodesInGroup(group, count, templateOptions());
200 }
201
202 @Override
203 public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, Template template)
204 throws RunNodesException {
205 checkNotNull(group, "group cannot be null");
206 checkNotNull(template.getLocation(), "location");
207 logger.debug(">> running %d node%s group(%s) location(%s) image(%s) hardwareProfile(%s) options(%s)", count,
208 count > 1 ? "s" : "", group, template.getLocation().getId(), template.getImage().getId(), template
209 .getHardware().getId(), template.getOptions());
210 Set<NodeMetadata> goodNodes = newLinkedHashSet();
211 Map<NodeMetadata, Exception> badNodes = newLinkedHashMap();
212 Multimap<NodeMetadata, CustomizationResponse> customizationResponses = LinkedHashMultimap.create();
213
214 Map<?, Future<Void>> responses = runNodesAndAddToSetStrategy.execute(group, count, template, goodNodes, badNodes,
215 customizationResponses);
216 Map<?, Exception> executionExceptions = awaitCompletion(responses, executor, null, logger, "runNodesWithTag("
217 + group + ")");
218 for (NodeMetadata node : concat(goodNodes, badNodes.keySet()))
219 if (node.getCredentials() != null)
220 credentialStore.put("node#" + node.getId(), node.getCredentials());
221 if (executionExceptions.size() > 0 || badNodes.size() > 0) {
222 throw new RunNodesException(group, count, template, goodNodes, executionExceptions, badNodes);
223 }
224 return goodNodes;
225 }
226
227 @Override
228 public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, TemplateOptions templateOptions)
229 throws RunNodesException {
230 return createNodesInGroup(group, count, templateBuilder().any().options(templateOptions).build());
231
232 }
233
234 @Override
235 public Set<? extends NodeMetadata> createNodesInGroup(String group, int count) throws RunNodesException {
236 return createNodesInGroup(group, count, templateOptions());
237 }
238
239
240
241
242 @Override
243 public void destroyNode(final String id) {
244 checkNotNull(id, "id");
245 logger.debug(">> destroying node(%s)", id);
246 final AtomicReference<NodeMetadata> node = new AtomicReference<NodeMetadata>();
247 RetryablePredicate<String> tester = new RetryablePredicate<String>(new Predicate<String>() {
248
249 @Override
250 public boolean apply(String input) {
251 try {
252 NodeMetadata md = destroyNodeStrategy.destroyNode(id);
253 if (md != null)
254 node.set(md);
255 return true;
256 } catch (IllegalStateException e) {
257 logger.warn("<< illegal state destroying node(%s)", id);
258 return false;
259 }
260 }
261
262 }, timeouts.nodeRunning, 1000, TimeUnit.MILLISECONDS);
263 boolean successful = tester.apply(id) && (node.get() == null || nodeTerminated.apply(node.get()));
264 if (successful)
265 credentialStore.remove("node#" + id);
266 logger.debug("<< destroyed node(%s) success(%s)", id, successful);
267 }
268
269
270
271
272 @Override
273 public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
274 logger.debug(">> destroying nodes matching(%s)", filter);
275 Set<NodeMetadata> set = newLinkedHashSet(transformParallel(nodesMatchingFilterAndNotTerminated(filter),
276 new Function<NodeMetadata, Future<NodeMetadata>>() {
277
278
279 @Override
280 public Future<NodeMetadata> apply(final NodeMetadata from) {
281 return executor.submit(new Callable<NodeMetadata>() {
282
283 @Override
284 public NodeMetadata call() throws Exception {
285 destroyNode(from.getId());
286 return from;
287 }
288
289 });
290 }
291
292 }, executor, null, logger, "destroyNodesMatching(" + filter + ")"));
293 logger.debug("<< destroyed(%d)", set.size());
294 return set;
295 }
296
297 Iterable<? extends NodeMetadata> nodesMatchingFilterAndNotTerminated(Predicate<NodeMetadata> filter) {
298 return filter(detailsOnAllNodes(), and(checkNotNull(filter, "filter"), not(TERMINATED)));
299 }
300
301
302
303
304
305 Iterable<? extends NodeMetadata> nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(
306 Predicate<NodeMetadata> filter) {
307 Iterable<? extends NodeMetadata> nodes = nodesMatchingFilterAndNotTerminated(filter);
308 if (Iterables.size(nodes) == 0)
309 throw new NoSuchElementException("no nodes matched filter: " + filter);
310 return nodes;
311 }
312
313
314
315
316 @Override
317 public Set<ComputeMetadata> listNodes() {
318 logger.debug(">> listing nodes");
319 Set<ComputeMetadata> set = newLinkedHashSet(listNodesStrategy.listNodes());
320 logger.debug("<< list(%d)", set.size());
321 return set;
322 }
323
324
325
326
327 @Override
328 public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter) {
329 checkNotNull(filter, "filter");
330 logger.debug(">> listing node details matching(%s)", filter);
331 Set<NodeMetadata> set = newLinkedHashSet(listNodesStrategy.listDetailsOnNodesMatching(filter));
332 logger.debug("<< list(%d)", set.size());
333 return set;
334 }
335
336
337
338
339 @Override
340 public Set<? extends Hardware> listHardwareProfiles() {
341 return hardwareProfiles.get();
342 }
343
344
345
346
347 @Override
348 public Set<? extends Image> listImages() {
349 return images.get();
350 }
351
352
353
354
355 @Override
356 public Set<? extends Location> listAssignableLocations() {
357 return locations.get();
358 }
359
360
361
362
363 @Override
364 public TemplateBuilder templateBuilder() {
365 return templateBuilderProvider.get();
366 }
367
368
369
370
371 @Override
372 public NodeMetadata getNodeMetadata(String id) {
373 checkNotNull(id, "id");
374 return getNodeMetadataStrategy.getNode(id);
375 }
376
377
378
379
380 @Override
381 public void rebootNode(String id) {
382 checkNotNull(id, "id");
383 logger.debug(">> rebooting node(%s)", id);
384 NodeMetadata node = rebootNodeStrategy.rebootNode(id);
385 boolean successful = nodeRunning.apply(node);
386 logger.debug("<< rebooted node(%s) success(%s)", id, successful);
387 }
388
389
390
391
392 @Override
393 public void rebootNodesMatching(Predicate<NodeMetadata> filter) {
394 logger.debug(">> rebooting nodes matching(%s)", filter);
395 transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter),
396 new Function<NodeMetadata, Future<Void>>() {
397
398 @Override
399 public Future<Void> apply(NodeMetadata from) {
400 rebootNode(from.getId());
401 return immediateFuture(null);
402 }
403
404 }, executor, null, logger, "rebootNodesMatching(" + filter + ")");
405 logger.debug("<< rebooted");
406 }
407
408
409
410
411 @Override
412 public void resumeNode(String id) {
413 checkNotNull(id, "id");
414 logger.debug(">> resuming node(%s)", id);
415 NodeMetadata node = resumeNodeStrategy.resumeNode(id);
416 boolean successful = nodeRunning.apply(node);
417 logger.debug("<< resumed node(%s) success(%s)", id, successful);
418 }
419
420
421
422
423 @Override
424 public void resumeNodesMatching(Predicate<NodeMetadata> filter) {
425 logger.debug(">> resuming nodes matching(%s)", filter);
426 transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter),
427 new Function<NodeMetadata, Future<Void>>() {
428
429 @Override
430 public Future<Void> apply(NodeMetadata from) {
431 resumeNode(from.getId());
432 return immediateFuture(null);
433 }
434
435 }, executor, null, logger, "resumeNodesMatching(" + filter + ")");
436 logger.debug("<< resumed");
437 }
438
439
440
441
442 @Override
443 public void suspendNode(String id) {
444 checkNotNull(id, "id");
445 logger.debug(">> suspending node(%s)", id);
446 NodeMetadata node = suspendNodeStrategy.suspendNode(id);
447 boolean successful = nodeSuspended.apply(node);
448 logger.debug("<< suspended node(%s) success(%s)", id, successful);
449 }
450
451
452
453
454 @Override
455 public void suspendNodesMatching(Predicate<NodeMetadata> filter) {
456 logger.debug(">> suspending nodes matching(%s)", filter);
457 transformParallel(nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter),
458 new Function<NodeMetadata, Future<Void>>() {
459
460 @Override
461 public Future<Void> apply(NodeMetadata from) {
462 suspendNode(from.getId());
463 return immediateFuture(null);
464 }
465
466 }, executor, null, logger, "suspendNodesMatching(" + filter + ")");
467 logger.debug("<< suspended");
468 }
469
470
471
472
473 @Override
474 public Map<NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, Payload runScript)
475 throws RunScriptOnNodesException {
476 return runScriptOnNodesMatching(filter, runScript, RunScriptOptions.NONE);
477 }
478
479
480
481
482 @Override
483 public Map<NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, Payload runScript,
484 RunScriptOptions options) throws RunScriptOnNodesException {
485 try {
486 return runScriptOnNodesMatching(filter, Statements.exec(Strings2.toStringAndClose(checkNotNull(runScript,
487 "runScript").getInput())), options);
488 } catch (IOException e) {
489 Throwables.propagate(e);
490 return null;
491 }
492 }
493
494
495
496
497 @Override
498 public Map<NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, String runScript)
499 throws RunScriptOnNodesException {
500 return runScriptOnNodesMatching(filter, Statements.exec(checkNotNull(runScript, "runScript")));
501 }
502
503
504
505
506 @Override
507 public Map<NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, Statement runScript)
508 throws RunScriptOnNodesException {
509 return runScriptOnNodesMatching(filter, runScript, RunScriptOptions.NONE);
510 }
511
512 @Override
513 public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
514 String runScript, RunScriptOptions options) throws RunScriptOnNodesException {
515 return runScriptOnNodesMatching(filter, Statements.exec(checkNotNull(runScript, "runScript")),
516 RunScriptOptions.NONE);
517 }
518
519
520
521
522 @Override
523 public Map<NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, Statement runScript,
524 RunScriptOptions options) throws RunScriptOnNodesException {
525
526 checkNotNull(filter, "filter");
527 checkNotNull(runScript, "runScript");
528 checkNotNull(options, "options");
529
530 Map<NodeMetadata, ExecResponse> goodNodes = newLinkedHashMap();
531 Map<NodeMetadata, Exception> badNodes = newLinkedHashMap();
532 Map<NodeMetadata, Future<ExecResponse>> responses = newLinkedHashMap();
533 Map<?, Exception> exceptions = ImmutableMap.<Object, Exception> of();
534
535 Iterable<? extends RunScriptOnNode> scriptRunners = transformNodesIntoInitializedScriptRunners(
536 nodesMatchingFilterAndNotTerminatedExceptionIfNotFound(filter), runScript, options, badNodes);
537 if (Iterables.size(scriptRunners) > 0) {
538 for (RunScriptOnNode runner : scriptRunners) {
539 responses.put(runner.getNode(), executor.submit(new RunScriptOnNodeAndAddToGoodMapOrPutExceptionIntoBadMap(
540 runner, goodNodes, badNodes)));
541 }
542 exceptions = awaitCompletion(responses, executor, null, logger, "runScriptOnNodesMatching(" + filter + ")");
543 }
544
545 if (exceptions.size() > 0 || badNodes.size() > 0) {
546 throw new RunScriptOnNodesException(runScript, options, goodNodes, exceptions, badNodes);
547 }
548 return goodNodes;
549 }
550
551 private Iterable<? extends RunScriptOnNode> transformNodesIntoInitializedScriptRunners(
552 Iterable<? extends NodeMetadata> nodes, Statement script, RunScriptOptions options,
553 Map<NodeMetadata, Exception> badNodes) {
554 return filter(transformParallel(nodes, new TransformNodesIntoInitializedScriptRunners(script, options, badNodes),
555 executor, null, logger, "initialize script runners"), notNull());
556 }
557
558 private Set<? extends NodeMetadata> detailsOnAllNodes() {
559 return newLinkedHashSet(listNodesStrategy.listDetailsOnNodesMatching(all()));
560 }
561
562 @Override
563 public TemplateOptions templateOptions() {
564 return templateOptionsProvider.get();
565 }
566
567 private final class TransformNodesIntoInitializedScriptRunners implements
568 Function<NodeMetadata, Future<RunScriptOnNode>> {
569 private final Map<NodeMetadata, Exception> badNodes;
570 private final Statement script;
571 private final RunScriptOptions options;
572
573 private TransformNodesIntoInitializedScriptRunners(Statement script, RunScriptOptions options,
574 Map<NodeMetadata, Exception> badNodes) {
575 this.badNodes = checkNotNull(badNodes, "badNodes");
576 this.script = checkNotNull(script, "script");
577 this.options = checkNotNull(options, "options");
578 }
579
580 @Override
581 public Future<RunScriptOnNode> apply(NodeMetadata node) {
582 checkNotNull(node, "node");
583 if (options.getOverrideCredentials() != null) {
584 node = NodeMetadataBuilder.fromNodeMetadata(node).credentials(options.getOverrideCredentials()).build();
585 }
586 return executor.submit(initScriptRunnerFactory.create(node, script, options, badNodes));
587 }
588 }
589
590 }