| 1 | /** | 
| 2 |  * | 
| 3 |  * Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com> | 
| 4 |  * | 
| 5 |  * ==================================================================== | 
| 6 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
| 7 |  * you may not use this file except in compliance with the License. | 
| 8 |  * You may obtain a copy of the License at | 
| 9 |  * | 
| 10 |  * http://www.apache.org/licenses/LICENSE-2.0 | 
| 11 |  * | 
| 12 |  * Unless required by applicable law or agreed to in writing, software | 
| 13 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
| 14 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| 15 |  * See the License for the specific language governing permissions and | 
| 16 |  * limitations under the License. | 
| 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 |  * @author Adrian Cole | 
| 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 |     * {@inheritDoc} | 
| 170 |     */ | 
| 171 |    @Override | 
| 172 |    public ComputeServiceContext getContext() { | 
| 173 |       return context; | 
| 174 |    } | 
| 175 |   | 
| 176 |    /** | 
| 177 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |                   // TODO make an async interface instead of re-wrapping | 
| 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 |     * @throws NoSuchElementException | 
| 303 |     *            if none found | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 338 |     */ | 
| 339 |    @Override | 
| 340 |    public Set<? extends Hardware> listHardwareProfiles() { | 
| 341 |       return hardwareProfiles.get(); | 
| 342 |    } | 
| 343 |   | 
| 344 |    /** | 
| 345 |     * {@inheritDoc} | 
| 346 |     */ | 
| 347 |    @Override | 
| 348 |    public Set<? extends Image> listImages() { | 
| 349 |       return images.get(); | 
| 350 |    } | 
| 351 |   | 
| 352 |    /** | 
| 353 |     * {@inheritDoc} | 
| 354 |     */ | 
| 355 |    @Override | 
| 356 |    public Set<? extends Location> listAssignableLocations() { | 
| 357 |       return locations.get(); | 
| 358 |    } | 
| 359 |   | 
| 360 |    /** | 
| 361 |     * {@inheritDoc} | 
| 362 |     */ | 
| 363 |    @Override | 
| 364 |    public TemplateBuilder templateBuilder() { | 
| 365 |       return templateBuilderProvider.get(); | 
| 366 |    } | 
| 367 |   | 
| 368 |    /** | 
| 369 |     * {@inheritDoc} | 
| 370 |     */ | 
| 371 |    @Override | 
| 372 |    public NodeMetadata getNodeMetadata(String id) { | 
| 373 |       checkNotNull(id, "id"); | 
| 374 |       return getNodeMetadataStrategy.getNode(id); | 
| 375 |    } | 
| 376 |   | 
| 377 |    /** | 
| 378 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |                   // TODO use native async | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |                   // TODO use native async | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |                   // TODO use native async | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 |     * {@inheritDoc} | 
| 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 | } |