View Javadoc

1   /**
2    * Licensed to jclouds, Inc. (jclouds) under one or more
3    * contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  jclouds licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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 }