| 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.strategy.impl; | 
| 20 |   | 
| 21 | import static com.google.common.base.Objects.toStringHelper; | 
| 22 | import static com.google.common.base.Preconditions.checkNotNull; | 
| 23 | import static com.google.common.collect.Iterables.any; | 
| 24 | import static com.google.common.collect.Maps.newLinkedHashMap; | 
| 25 | import static com.google.common.collect.Sets.newLinkedHashSet; | 
| 26 | import static org.jclouds.concurrent.Futures.compose; | 
| 27 |   | 
| 28 | import java.security.SecureRandom; | 
| 29 | import java.util.Map; | 
| 30 | import java.util.Set; | 
| 31 | import java.util.concurrent.Callable; | 
| 32 | import java.util.concurrent.ExecutorService; | 
| 33 | import java.util.concurrent.Future; | 
| 34 |   | 
| 35 | import javax.annotation.Resource; | 
| 36 | import javax.inject.Inject; | 
| 37 | import javax.inject.Named; | 
| 38 | import javax.inject.Singleton; | 
| 39 |   | 
| 40 | import org.jclouds.Constants; | 
| 41 | import org.jclouds.compute.config.CustomizationResponse; | 
| 42 | import org.jclouds.compute.domain.ComputeMetadata; | 
| 43 | import org.jclouds.compute.domain.NodeMetadata; | 
| 44 | import org.jclouds.compute.domain.Template; | 
| 45 | import org.jclouds.compute.reference.ComputeServiceConstants; | 
| 46 | import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName; | 
| 47 | import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap; | 
| 48 | import org.jclouds.compute.strategy.ListNodesStrategy; | 
| 49 | import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; | 
| 50 | import org.jclouds.logging.Logger; | 
| 51 |   | 
| 52 | import com.google.common.base.Predicate; | 
| 53 | import com.google.common.collect.Multimap; | 
| 54 |   | 
| 55 | /** | 
| 56 |  * creates futures that correlate to | 
| 57 |  *  | 
| 58 |  * @author Adrian Cole | 
| 59 |  */ | 
| 60 | @Singleton | 
| 61 | public class CreateNodesWithGroupEncodedIntoNameThenAddToSet implements CreateNodesInGroupThenAddToSet { | 
| 62 |   | 
| 63 |    private class AddNode implements Callable<NodeMetadata> { | 
| 64 |       private final String name; | 
| 65 |       private final String tag; | 
| 66 |       private final Template template; | 
| 67 |   | 
| 68 |       private AddNode(String name, String tag, Template template) { | 
| 69 |          this.name = checkNotNull(name, "name"); | 
| 70 |          this.tag = checkNotNull(tag, "tag"); | 
| 71 |          this.template = checkNotNull(template, "template"); | 
| 72 |       } | 
| 73 |   | 
| 74 |       @Override | 
| 75 |       public NodeMetadata call() throws Exception { | 
| 76 |          NodeMetadata node = null; | 
| 77 |          logger.debug(">> adding node location(%s) name(%s) image(%s) hardware(%s)", | 
| 78 |                   template.getLocation().getId(), name, template.getImage().getProviderId(), template.getHardware() | 
| 79 |                            .getProviderId()); | 
| 80 |          node = addNodeWithTagStrategy.createNodeWithGroupEncodedIntoName(tag, name, template); | 
| 81 |          logger.debug("<< %s node(%s)", node.getState(), node.getId()); | 
| 82 |          return node; | 
| 83 |       } | 
| 84 |   | 
| 85 |       public String toString() { | 
| 86 |          return toStringHelper(this).add("name", name).add("tag", tag).add("template", template).toString(); | 
| 87 |       } | 
| 88 |   | 
| 89 |    } | 
| 90 |   | 
| 91 |    @Resource | 
| 92 |    @Named(ComputeServiceConstants.COMPUTE_LOGGER) | 
| 93 |    protected Logger logger = Logger.NULL; | 
| 94 |    protected final CreateNodeWithGroupEncodedIntoName addNodeWithTagStrategy; | 
| 95 |    protected final ListNodesStrategy listNodesStrategy; | 
| 96 |    protected final String nodeNamingConvention; | 
| 97 |    protected final ExecutorService executor; | 
| 98 |    protected final CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory; | 
| 99 |   | 
| 100 |    @Inject | 
| 101 |    protected CreateNodesWithGroupEncodedIntoNameThenAddToSet( | 
| 102 |             CreateNodeWithGroupEncodedIntoName addNodeWithTagStrategy, | 
| 103 |             ListNodesStrategy listNodesStrategy, | 
| 104 |             @Named("NAMING_CONVENTION") String nodeNamingConvention, | 
| 105 |             @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, | 
| 106 |             CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory) { | 
| 107 |       this.addNodeWithTagStrategy = addNodeWithTagStrategy; | 
| 108 |       this.listNodesStrategy = listNodesStrategy; | 
| 109 |       this.nodeNamingConvention = nodeNamingConvention; | 
| 110 |       this.executor = executor; | 
| 111 |       this.customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory = customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory; | 
| 112 |    } | 
| 113 |   | 
| 114 |    /** | 
| 115 |     * This implementation gets a list of acceptable node names to encode the tag into, then it | 
| 116 |     * simultaneously runs the nodes and applies options to them. | 
| 117 |     */ | 
| 118 |    @Override | 
| 119 |    public Map<?, Future<Void>> execute(String tag, int count, Template template, Set<NodeMetadata> goodNodes, | 
| 120 |             Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) { | 
| 121 |       Map<String, Future<Void>> responses = newLinkedHashMap(); | 
| 122 |       for (String name : getNextNames(tag, template, count)) { | 
| 123 |          responses.put(name, compose(executor.submit(new AddNode(name, tag, template)), | 
| 124 |                   customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory.create(template.getOptions(), | 
| 125 |                            goodNodes, badNodes, customizationResponses), executor)); | 
| 126 |       } | 
| 127 |       return responses; | 
| 128 |    } | 
| 129 |   | 
| 130 |    /** | 
| 131 |     * Find the next node names that can be used. These will be derived from the tag and the | 
| 132 |     * template. We will pre-allocate a specified quantity, and attempt to verify that there is no | 
| 133 |     * name conflict with the current service. | 
| 134 |     *  | 
| 135 |     * @param tag | 
| 136 |     * @param count | 
| 137 |     * @param template | 
| 138 |     * @return | 
| 139 |     */ | 
| 140 |    protected Set<String> getNextNames(final String tag, final Template template, int count) { | 
| 141 |       Set<String> names = newLinkedHashSet(); | 
| 142 |       Iterable<? extends ComputeMetadata> currentNodes = listNodesStrategy.listNodes(); | 
| 143 |       int maxTries = 100; | 
| 144 |       int currentTries = 0; | 
| 145 |       while (names.size() < count && currentTries++ < maxTries) { | 
| 146 |          final String name = getNextName(tag, template); | 
| 147 |          if (!any(currentNodes, new Predicate<ComputeMetadata>() { | 
| 148 |   | 
| 149 |             @Override | 
| 150 |             public boolean apply(ComputeMetadata input) { | 
| 151 |                return name.equals(input.getName()); | 
| 152 |             } | 
| 153 |   | 
| 154 |          })) { | 
| 155 |             names.add(name); | 
| 156 |          } | 
| 157 |       } | 
| 158 |       return names; | 
| 159 |    } | 
| 160 |   | 
| 161 |    /** | 
| 162 |     * Get a name using a random mechanism that still ties all nodes in a tag together. | 
| 163 |     *  | 
| 164 |     * This implementation will pass the tag and a hex formatted random number to the configured | 
| 165 |     * naming convention. | 
| 166 |     *  | 
| 167 |     */ | 
| 168 |    protected String getNextName(final String tag, final Template template) { | 
| 169 |       return String.format(nodeNamingConvention, tag, Integer.toHexString(new SecureRandom().nextInt(4095))); | 
| 170 |    } | 
| 171 |   | 
| 172 | } |