JCLOUDS-381. Allow explicit naming of nodes via TemplateOptions.

This commit is contained in:
Andrew Bayer 2013-11-15 12:59:19 -08:00
parent 3ecbf90847
commit 23e43b2c8d
33 changed files with 551 additions and 124 deletions

View File

@ -18,6 +18,7 @@ package org.jclouds.cloudservers.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromCommaDelimitedValue;
import static org.jclouds.compute.util.ComputeServiceUtils.groupFromMapOrName;
import java.util.Map;
import java.util.NoSuchElementException;
@ -111,7 +112,7 @@ public class ServerToNodeMetadata implements Function<Server, NodeMetadata> {
builder.location(new LocationBuilder().scope(LocationScope.HOST).id(from.getHostId()).description(
from.getHostId()).parent(location.get()).build());
addMetadataAndParseTagsFromCommaDelimitedValue(builder, from.getMetadata());
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getName()));
builder.group(groupFromMapOrName(from.getMetadata(), from.getName(), nodeNamingConvention));
builder.imageId(from.getImageId() + "");
builder.operatingSystem(parseOperatingSystem(from));
builder.hardware(parseHardware(from));

View File

@ -34,6 +34,7 @@ import org.jclouds.cloudservers.domain.Server;
import org.jclouds.cloudservers.options.ListOptions;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
@ -59,6 +60,7 @@ public class CloudServersComputeServiceAdapter implements ComputeServiceAdapter<
@Override
public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(String group, String name,
Template template) {
template.getOptions().userMetadata(ComputeServiceConstants.NODE_GROUP_KEY, group);
Server server = client
.createServer(name, Integer.parseInt(template.getImage().getProviderId()), Integer.parseInt(template
.getHardware().getProviderId()), withMetadata(metadataAndTagsAsCommaDelimitedValue(template.getOptions())));

View File

@ -106,6 +106,14 @@ public class CloudSigmaTemplateOptions extends TemplateOptions implements Clonea
return CloudSigmaTemplateOptions.class.cast(options.userMetadata(userMetadata));
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static CloudSigmaTemplateOptions nodeNames(Iterable<String> nodeNames) {
CloudSigmaTemplateOptions options = new CloudSigmaTemplateOptions();
return CloudSigmaTemplateOptions.class.cast(options.nodeNames(nodeNames));
}
public static CloudSigmaTemplateOptions overrideLoginUser(String user) {
CloudSigmaTemplateOptions options = new CloudSigmaTemplateOptions();
return options.overrideLoginUser(user);
@ -262,6 +270,14 @@ public class CloudSigmaTemplateOptions extends TemplateOptions implements Clonea
return CloudSigmaTemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public CloudSigmaTemplateOptions nodeNames(Iterable<String> nodeNames) {
return CloudSigmaTemplateOptions.class.cast(super.nodeNames(nodeNames));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -104,7 +104,9 @@ public class VirtualMachineToNodeMetadata implements Function<VirtualMachine, No
// on hosts not started with jclouds
builder.hostname(from.getDisplayName());
builder.location(FluentIterable.from(locations.get()).firstMatch(idEquals(from.getZoneId())).orNull());
if (from.getDisplayName() != null) {
if (from.getGroup() != null) {
builder.group(from.getGroup());
} else if (from.getDisplayName() != null) {
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getDisplayName()));
}
Image image = FluentIterable.from(images.get()).firstMatch(new Predicate<Image>() {

View File

@ -401,6 +401,14 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
CloudStackTemplateOptions options = new CloudStackTemplateOptions();
return CloudStackTemplateOptions.class.cast(options.userMetadata(key, value));
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static CloudStackTemplateOptions nodeNames(Iterable<String> nodeNames) {
CloudStackTemplateOptions options = new CloudStackTemplateOptions();
return CloudStackTemplateOptions.class.cast(options.nodeNames(nodeNames));
}
}
// methods that only facilitate returning the correct object type
@ -452,4 +460,12 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
public CloudStackTemplateOptions userMetadata(String key, String value) {
return CloudStackTemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public CloudStackTemplateOptions nodeNames(Iterable<String> nodeNames) {
return CloudStackTemplateOptions.class.cast(super.nodeNames(nodeNames));
}
}

View File

@ -165,6 +165,8 @@ public class CloudStackComputeServiceAdapter implements
OptionsConverter optionsConverter = optionsConverters.get(zone.getNetworkType());
options = optionsConverter.apply(templateOptions, networks, zoneId, options);
options.group(group);
if (templateOptions.getIpOnDefaultNetwork() != null) {
options.ipOnDefaultNetwork(templateOptions.getIpOnDefaultNetwork());
}

View File

@ -94,19 +94,20 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
public void testCreateNodeWithGroupEncodedIntoName() {
HttpRequest deployVM = HttpRequest.builder().method("GET")
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "1")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "4")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "wJ%2BiflOS3am5qcjQOd8Y/Pw8/Dc%3D")
.addHeader("Accept", "application/json")
.build();
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "1")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "4")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("group", "test")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "M2Wx0CgOeH9vYHhbcbblwziqpwI%3D")
.addHeader("Accept", "application/json")
.build();
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
.put(listTemplates, listTemplatesResponse)
@ -136,20 +137,21 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPair() throws IOException {
HttpRequest deployVM = HttpRequest.builder().method("GET")
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "1")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "4")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "hI/U4cWXdU6KTZKbJvzPCmOpGmU%3D")
.addHeader("Accept", "application/json")
.build();
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "1")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "4")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("group", "test")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "D3qQlTNjxrBXeG82C7YPrwU1jMc%3D")
.addHeader("Accept", "application/json")
.build();
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
.put(listTemplates, listTemplatesResponse)
@ -182,20 +184,21 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
public void testCreateNodeWithGroupEncodedIntoNameWithGenerateKeyPair() throws IOException {
HttpRequest deployVM = HttpRequest.builder().method("GET")
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "1")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "4")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("keypair", "jclouds-test")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "4M9C8IjohDDKFMAXQSX3mjXpYvM%3D")
.addHeader("Accept", "application/json")
.build();
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "1")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "4")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("group", "test")
.addQueryParam("keypair", "jclouds-test")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "kfU/J/REa4DdYj0b/pSjuB3h3Qc%3D")
.addHeader("Accept", "application/json")
.build();
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
.put(listTemplates, listTemplatesResponse)
@ -227,20 +230,21 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairDefaultSecurityGroup() throws IOException {
HttpRequest deployVM = HttpRequest.builder().method("GET")
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "2")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "241")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "Ar2B/ZVxMO2078cP0XliWWR4cQ0%3D")
.addHeader("Accept", "application/json")
.build();
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "2")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "241")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("group", "test")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "5qkUSGh0y%2BP/t04/j3%2BEN9PAeFI%3D")
.addHeader("Accept", "application/json")
.build();
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
.put(listTemplates, listTemplatesResponse)
@ -285,11 +289,12 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("group", "test")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("diskofferingid", "5678")
.addQueryParam("size", "10")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "FWWCEpsrbbjxiqoQve302rrfOjI%3D")
.addQueryParam("signature", "lDzBXtVKCktueskyI/haID9ohJU%3D")
.addHeader("Accept", "application/json")
.build();
@ -330,21 +335,22 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairGenerateSecurityGroup() throws IOException {
HttpRequest deployVM = HttpRequest.builder().method("GET")
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "2")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "241")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("securitygroupids", "30")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "yNAiMYw3RstNj979udttALOHxfU%3D")
.addHeader("Accept", "application/json")
.build();
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "2")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "241")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("networkids", "204")
.addQueryParam("group", "test")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("securitygroupids", "30")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "rz8V/tMk/UbxUhNqp7Bq3CrSg/k%3D")
.addHeader("Accept", "application/json")
.build();
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
.put(listTemplates, listTemplatesResponse)
@ -386,23 +392,24 @@ public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackCom
public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairAssignedToAccountAndDomain() throws IOException {
HttpRequest deployVM = HttpRequest.builder().method("GET")
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "1")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "4")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("account", "account") //
.addQueryParam("domainid", "domainId") //
.addQueryParam("networkids", "204")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "ly5Pip8ICOoVTmNLdDBTc3gbKlA%3D")
.addHeader("Accept", "application/json")
.build();
.endpoint("http://localhost:8080/client/api")
.addQueryParam("response", "json")
.addQueryParam("command", "deployVirtualMachine")
.addQueryParam("zoneid", "1")
.addQueryParam("serviceofferingid", "1")
.addQueryParam("templateid", "4")
.addQueryParam("displayname", "test-e92")
.addQueryParam("name", "test-e92")
.addQueryParam("account", "account") //
.addQueryParam("domainid", "domainId") //
.addQueryParam("networkids", "204")
.addQueryParam("group", "test")
.addQueryParam("keypair", "mykeypair")
.addQueryParam("apiKey", "APIKEY")
.addQueryParam("signature", "hGV6gZZakwvNKhTJurkm48%2Bzgso%3D")
.addHeader("Accept", "application/json")
.build();
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
.put(listTemplates, listTemplatesResponse)
.put(listOsTypes, listOsTypesResponse)

View File

@ -32,6 +32,7 @@ import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_GENERATE_INSTA
import static org.jclouds.ec2.util.Tags.resourceToTagsAsMap;
import static org.jclouds.util.Predicates2.retry;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@ -96,6 +97,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultimap.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListeningExecutorService;
@ -153,8 +155,9 @@ public class EC2ComputeService extends BaseComputeService {
if (client.getTagApiForRegion(region).isPresent()) {
Map<String, String> common = metadataAndTagsAsValuesOfEmptyString(template.getOptions());
if (common.size() > 0 || generateInstanceNames) {
return addTagsToInstancesInRegion(common, nodes, region, group);
if (generateInstanceNames || !common.isEmpty() || !template.getOptions().getNodeNames().isEmpty()) {
return addTagsAndNamesToInstancesInRegion(common, template.getOptions().getNodeNames(),
nodes, region, group);
}
}
return nodes;
@ -167,17 +170,23 @@ public class EC2ComputeService extends BaseComputeService {
}
};
private Set<NodeMetadata> addTagsToInstancesInRegion(Map<String, String> common, Set<? extends NodeMetadata> input,
String region, String group) {
private Set<NodeMetadata> addTagsAndNamesToInstancesInRegion(Map<String, String> common, Set<String> nodeNames,
Set<? extends NodeMetadata> input, String region,
String group) {
Map<String, ? extends NodeMetadata> instancesById = Maps.uniqueIndex(input, instanceId);
ImmutableSet.Builder<NodeMetadata> builder = ImmutableSet.<NodeMetadata> builder();
if (generateInstanceNames && !common.containsKey("Name")) {
for (Map.Entry<String, ? extends NodeMetadata> entry : instancesById.entrySet()) {
String id = entry.getKey();
NodeMetadata instance = entry.getValue();
String name;
if (!nodeNames.isEmpty()) {
name = Iterables.get(nodeNames, 0);
} else {
name = id.replaceAll(".*-", group + "-");
}
Map<String, String> tags = ImmutableMap.<String, String> builder().putAll(common)
.put("Name", id.replaceAll(".*-", group + "-")).build();
.put("Name", name).build();
logger.debug(">> applying tags %s to instance %s in region %s", tags, id, region);
client.getTagApiForRegion(region).get().applyToResources(tags, ImmutableSet.of(id));
builder.add(addTagsForInstance(tags, instancesById.get(id)));

View File

@ -324,6 +324,14 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.userMetadata(userMetadata));
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static EC2TemplateOptions nodeNames(Iterable<String> nodeNames) {
EC2TemplateOptions options = new EC2TemplateOptions();
return EC2TemplateOptions.class.cast(options.nodeNames(nodeNames));
}
public static EC2TemplateOptions overrideLoginUser(String user) {
EC2TemplateOptions options = new EC2TemplateOptions();
@ -521,6 +529,14 @@ public class EC2TemplateOptions extends TemplateOptions implements Cloneable {
return EC2TemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public EC2TemplateOptions nodeNames(Iterable<String> nodeNames) {
return EC2TemplateOptions.class.cast(super.nodeNames(nodeNames));
}
/**
* {@inheritDoc}
*/

View File

@ -15,10 +15,13 @@
* limitations under the License.
*/
package org.jclouds.ec2.compute;
import static org.jclouds.ec2.compute.options.EC2TemplateOptions.Builder.blockUntilRunning;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import javax.ws.rs.core.MediaType;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.ec2.compute.internal.BaseEC2ComputeServiceExpectTest;
@ -28,6 +31,7 @@ import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
@ -57,12 +61,63 @@ public class EC2ComputeServiceExpectTest extends BaseEC2ComputeServiceExpectTest
ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build());
NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1,
blockUntilRunning(false).overrideLoginUser("ec2-user")));
blockUntilRunning(false).overrideLoginUser("ec2-user")));
assertEquals(node.getCredentials().getUser(), "ec2-user");
System.out.println(node.getImageId());
assertNotNull(node.getCredentials().getPrivateKey());
}
public void testCreateNodeWithSpecifiedName() throws Exception {
HttpRequest createNamedTagsRequest =
formSigner.filter(HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateTags" +
"&ResourceId.1=i-2baa5550" +
"&Signature=Trp5e5%2BMqeBeBZbLYa9s9gxahQ9nkx6ETfsGl82IV8Y%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Tag.1.Key=Name" +
"&Tag.1.Value=test-node" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build());
HttpResponse describeNamedInstanceResponse =
HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType(
"/describe_instances_running-named.xml", MediaType.APPLICATION_XML)).build();
Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder();
requestResponseMap.put(describeRegionsRequest, describeRegionsResponse);
requestResponseMap.put(describeAvailabilityZonesRequest, describeAvailabilityZonesResponse);
requestResponseMap.put(describeImagesRequest, describeImagesResponse);
requestResponseMap.put(createKeyPairRequest, createKeyPairResponse);
requestResponseMap.put(createSecurityGroupRequest, createSecurityGroupResponse);
requestResponseMap.put(describeSecurityGroupRequest, describeSecurityGroupResponse);
requestResponseMap.put(authorizeSecurityGroupIngressRequest22, authorizeSecurityGroupIngressResponse);
requestResponseMap.put(authorizeSecurityGroupIngressRequestGroup, authorizeSecurityGroupIngressResponse);
requestResponseMap.put(runInstancesRequest, runInstancesResponse);
requestResponseMap.put(describeInstanceRequest, describeNamedInstanceResponse);
requestResponseMap.put(describeInstanceMultiIdsRequest, describeInstanceMultiIdsResponse);
requestResponseMap.put(describeImageRequest, describeImagesResponse);
requestResponseMap.put(createNamedTagsRequest, createTagsResponse);
ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build());
NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1,
blockUntilRunning(false).overrideLoginUser("ec2-user").nodeNames(ImmutableSet.of("test-node"))));
assertEquals(node.getCredentials().getUser(), "ec2-user");
assertNotNull(node.getCredentials().getPrivateKey());
assertEquals(node.getName(), "test-node");
}
//FIXME - issue-1051
@Test(enabled = false)
public void testCreateNodeWithGeneratedKeyPairAndOverriddenLoginUserWithTemplateBuilder() throws Exception {

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
<requestId>f6d3252e-35e5-4ef5-b2c5-62da95dd829b</requestId>
<reservationSet>
<item>
<reservationId>r-205ad944</reservationId>
<ownerId>993194456877</ownerId>
<groupSet>
<item>
<groupId>sg-3c6ef654</groupId>
<groupName>jclouds#mygroup2</groupName>
</item>
</groupSet>
<instancesSet>
<item>
<instanceId>i-2baa5550</instanceId>
<imageId>ami-aecd60c7</imageId>
<instanceState>
<code>16</code>
<name>running</name>
</instanceState>
<privateDnsName>ip-10-28-89-195.ec2.internal</privateDnsName>
<dnsName>ec2-50-16-1-166.compute-1.amazonaws.com</dnsName>
<reason/>
<keyName>jclouds#mygroup2#81</keyName>
<amiLaunchIndex>0</amiLaunchIndex>
<productCodes/>
<instanceType>t1.micro</instanceType>
<launchTime>2012-08-02T04:28:30.000Z</launchTime>
<placement>
<availabilityZone>us-east-1e</availabilityZone>
<groupName/>
<tenancy>default</tenancy>
</placement>
<kernelId>aki-88aa75e1</kernelId>
<monitoring>
<state>disabled</state>
</monitoring>
<privateIpAddress>10.28.89.195</privateIpAddress>
<ipAddress>50.16.1.166</ipAddress>
<groupSet>
<item>
<groupId>sg-3c6ef654</groupId>
<groupName>jclouds#mygroup2</groupName>
</item>
</groupSet>
<architecture>x86_64</architecture>
<rootDeviceType>ebs</rootDeviceType>
<rootDeviceName>/dev/sda1</rootDeviceName>
<blockDeviceMapping>
<item>
<deviceName>/dev/sda1</deviceName>
<ebs>
<volumeId>vol-f2d7c993</volumeId>
<status>attached</status>
<attachTime>2012-08-02T04:28:56.000Z</attachTime>
<deleteOnTermination>true</deleteOnTermination>
</ebs>
</item>
</blockDeviceMapping>
<virtualizationType>paravirtual</virtualizationType>
<clientToken/>
<tagSet>
<item>
<key>Name</key>
<value>test-node</value>
</item>
</tagSet>
<hypervisor>xen</hypervisor>
</item>
</instancesSet>
</item>
</reservationSet>
</DescribeInstancesResponse>

View File

@ -120,6 +120,8 @@ public class ElasticStackComputeServiceAdapter implements
throw new IllegalStateException("could not image drive in time!");
}
template.getOptions().userMetadata(ComputeServiceConstants.NODE_GROUP_KEY, tag);
Server toCreate = small(name, drive.getUuid(), defaultVncPassword).mem(template.getHardware().getRam())
.cpu((int) (template.getHardware().getProcessors().get(0).getSpeed()))
.tags(template.getOptions().getTags()).userMetadata(template.getOptions().getUserMetadata()).build();

View File

@ -18,6 +18,7 @@ package org.jclouds.elasticstack.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.compute.predicates.ImagePredicates.idEquals;
import static org.jclouds.compute.util.ComputeServiceUtils.groupFromMapOrName;
import java.util.Map;
import java.util.Set;
@ -92,7 +93,7 @@ public class ServerInfoToNodeMetadata implements Function<ServerInfo, NodeMetada
builder.ids(from.getUuid());
builder.name(from.getName());
builder.location(locationSupplier.get());
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getName()));
builder.group(groupFromMapOrName(from.getUserMetadata(), from.getName(), nodeNamingConvention));
builder.tags(from.getTags());
builder.userMetadata(from.getUserMetadata());
String imageId = getImageIdFromServer.apply(from);

View File

@ -22,6 +22,7 @@ import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Iterables.transform;
import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromCommaDelimitedValue;
import static org.jclouds.compute.util.ComputeServiceUtils.groupFromMapOrName;
import java.net.Inet4Address;
import java.util.Map;
@ -100,8 +101,8 @@ public class ServerInZoneToNodeMetadata implements Function<ServerInZone, NodeMe
builder.hostname(from.getName());
builder.location(from.getHostId() != null ? new LocationBuilder().scope(LocationScope.HOST).id(from.getHostId())
.description(from.getHostId()).parent(zone).build() : zone);
builder.group(groupFromMapOrName(from.getMetadata(), from.getName(), nodeNamingConvention));
addMetadataAndParseTagsFromCommaDelimitedValue(builder, from.getMetadata());
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getName()));
builder.imageId(ZoneAndId.fromZoneAndId(serverInZone.getZone(), from.getImage().getId()).slashEncode());
builder.operatingSystem(findOperatingSystemForServerOrNull(serverInZone));
builder.hardware(findHardwareForServerOrNull(serverInZone));

View File

@ -299,6 +299,14 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
return NovaTemplateOptions.class.cast(options.userMetadata(userMetadata));
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static NovaTemplateOptions nodeNames(Iterable<String> nodeNames) {
NovaTemplateOptions options = new NovaTemplateOptions();
return NovaTemplateOptions.class.cast(options.nodeNames(nodeNames));
}
/**
* @see TemplateOptions#overrideLoginUser
*/
@ -494,6 +502,15 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
return NovaTemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public NovaTemplateOptions nodeNames(Iterable<String> nodeNames) {
return NovaTemplateOptions.class.cast(super.nodeNames(nodeNames));
}
/**
* User data as bytes (not base64-encoded)
*/

View File

@ -35,6 +35,7 @@ import org.jclouds.compute.config.CustomizationResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
import org.jclouds.compute.strategy.ListNodesStrategy;
@ -142,6 +143,7 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
templateOptions.securityGroupNames(securityGroupName);
}
}
templateOptions.userMetadata(ComputeServiceConstants.NODE_GROUP_KEY, group);
return super.execute(group, count, mutableTemplate, goodNodes, badNodes, customizationResponses);
}

View File

@ -239,7 +239,8 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
.addHeader("X-Auth-Token", authToken)
.payload(
payloadFromStringWithContentType(
"{\"server\":{\"name\":\"test-1\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"key_name\":\"jclouds-test-0\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
"{\"server\":{\"name\":\"test-1\",\"imageRef\":\"14\",\"flavorRef\":\"1\"," +
"\"metadata\":{\"jclouds-group\":\"test\"},\"key_name\":\"jclouds-test-0\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
"application/json")).build();
HttpResponse createdServer = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
@ -293,7 +294,8 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
.addHeader("X-Auth-Token", authToken)
.payload(
payloadFromStringWithContentType(
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"key_name\":\"fooPair\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\"," +
"\"metadata\":{\"jclouds-group\":\"test\"},\"key_name\":\"fooPair\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
"application/json")).build();
HttpResponse createdServer = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
@ -343,7 +345,8 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
.addHeader("X-Auth-Token", authToken)
.payload(
payloadFromStringWithContentType(
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"key_name\":\"fooPair\",\"security_groups\":[{\"name\":\"mygroup\"}]}}",
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\"," +
"\"metadata\":{\"jclouds-group\":\"test\"},\"key_name\":\"fooPair\",\"security_groups\":[{\"name\":\"mygroup\"}]}}",
"application/json")).build();
HttpResponse createdServer = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")

View File

@ -21,6 +21,7 @@ import static com.google.common.base.Predicates.not;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Iterables.filter;
import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromCommaDelimitedValue;
import static org.jclouds.compute.util.ComputeServiceUtils.groupFromMapOrName;
import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.getCredentialsFrom;
import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.getIpsFromVApp;
import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.toComputeOs;
@ -44,6 +45,7 @@ import org.jclouds.vcloud.domain.VApp;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
/** @author Adrian Cole */
@Singleton
@ -73,19 +75,26 @@ public class VAppToNodeMetadata implements Function<VApp, NodeMetadata> {
builder.ids(from.getHref().toASCIIString());
builder.uri(from.getHref());
builder.name(from.getName());
String groupName = "";
Map<String, String> metadataMap;
if (!isNullOrEmpty(from.getDescription())
&& from.getDescription().indexOf('=') != -1
&& from.getDescription().indexOf('\n') != -1) {
try {
addMetadataAndParseTagsFromCommaDelimitedValue(builder,
Splitter.on('\n').withKeyValueSeparator("=").split(from.getDescription()));
metadataMap = Splitter.on('\n').withKeyValueSeparator("=").split(from.getDescription());
addMetadataAndParseTagsFromCommaDelimitedValue(builder, metadataMap);
} catch (IllegalArgumentException iae) {
// no op
metadataMap = ImmutableMap.of();
}
} else {
metadataMap = ImmutableMap.of();
}
builder.hostname(from.getName());
builder.location(findLocationForResourceInVDC.apply(from.getVDC()));
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getName()));
builder.group(groupFromMapOrName(metadataMap, from.getName(), nodeNamingConvention));
builder.operatingSystem(toComputeOs(from, null));
builder.hardware(hardwareForVApp.apply(from));
builder.status(vAppStatusToNodeStatus.get(from.getStatus()));

View File

@ -223,6 +223,14 @@ public class VCloudTemplateOptions extends TemplateOptions implements Cloneable
return VCloudTemplateOptions.class.cast(options.userMetadata(key, value));
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static VCloudTemplateOptions nodeNames(Iterable<String> nodeNames) {
VCloudTemplateOptions options = new VCloudTemplateOptions();
return VCloudTemplateOptions.class.cast(options.nodeNames(nodeNames));
}
}
/**
@ -316,4 +324,12 @@ public class VCloudTemplateOptions extends TemplateOptions implements Cloneable
return VCloudTemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public VCloudTemplateOptions nodeNames(Iterable<String> nodeNames) {
return VCloudTemplateOptions.class.cast(super.nodeNames(nodeNames));
}
}

View File

@ -124,6 +124,7 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA
// per above check, we know there is only a single VM
Vm vm = get(vAppResponse.getChildren(), 0);
template.getOptions().userMetadata(ComputeServiceConstants.NODE_GROUP_KEY, group);
VCloudTemplateOptions vOptions = VCloudTemplateOptions.class.cast(template.getOptions());
// note we cannot do tasks in parallel or VCD will throw "is busy" errors

View File

@ -82,6 +82,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
to.userMetadata(this.getUserMetadata());
if (this.getTags().size() > 0)
to.tags(getTags());
if (!this.getNodeNames().isEmpty())
to.nodeNames(getNodeNames());
if (!this.shouldBlockUntilRunning())
to.blockUntilRunning(false);
if (!this.shouldBlockOnComplete())
@ -296,6 +298,16 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
throw new IllegalArgumentException("tags are immutable");
}
@Override
public Set<String> getNodeNames() {
return delegate.getNodeNames();
}
@Override
public TemplateOptions nodeNames(Iterable<String> nodeNames) {
throw new IllegalArgumentException("nodeNames are immutable");
}
@Override
public Set<String> getGroups() {
return delegate.getGroups();
@ -303,12 +315,12 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
@Override
public TemplateOptions securityGroups(Iterable<String> securityGroups) {
throw new IllegalArgumentException("tags are immutable");
throw new IllegalArgumentException("security groups are immutable");
}
@Override
public TemplateOptions securityGroups(String... securityGroups) {
throw new IllegalArgumentException("tags are immutable");
throw new IllegalArgumentException("security groups are immutable");
}
@Override
@ -348,7 +360,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
protected Map<String, String> userMetadata = Maps.newLinkedHashMap();
@Override
protected Set<String> nodeNames = ImmutableSet.of();
public boolean equals(Object o) {
if (this == o)
return true;
@ -356,15 +369,16 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
return false;
TemplateOptions that = TemplateOptions.class.cast(o);
return super.equals(that) && equal(this.inboundPorts, that.inboundPorts) && equal(this.script, that.script)
&& equal(this.publicKey, that.publicKey) && equal(this.privateKey, that.privateKey)
&& equal(this.blockUntilRunning, that.blockUntilRunning) && equal(this.tags, that.tags)
&& equal(this.securityGroups, that.securityGroups) && equal(this.userMetadata, that.userMetadata);
&& equal(this.publicKey, that.publicKey) && equal(this.privateKey, that.privateKey)
&& equal(this.blockUntilRunning, that.blockUntilRunning) && equal(this.tags, that.tags)
&& equal(this.securityGroups, that.securityGroups) && equal(this.userMetadata, that.userMetadata)
&& equal(this.nodeNames, that.nodeNames);
}
@Override
public int hashCode() {
return Objects.hashCode(super.hashCode(), inboundPorts, script, publicKey, privateKey, blockUntilRunning, tags,
securityGroups, userMetadata);
securityGroups, userMetadata, nodeNames);
}
@Override
@ -382,6 +396,8 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
toString.add("blockUntilRunning", blockUntilRunning);
if (tags.size() != 0)
toString.add("tags", tags);
if (!nodeNames.isEmpty())
toString.add("nodeNames", nodeNames);
if (securityGroups.size() != 0)
toString.add("securityGroups", securityGroups);
if (userMetadata.size() != 0)
@ -401,6 +417,10 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
return tags;
}
public Set<String> getNodeNames() {
return nodeNames;
}
public Set<String> getGroups() {
return securityGroups;
}
@ -475,6 +495,19 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
return this;
}
/**
* specifies names to be used for the created nodes.
*
* Note that this does not guarantee uniqueness - if there are already existing nodes with a name
* specified here, there will still be a new node created with the same name. Also, if more
* nodes are to be created than there are names, subsequent names will use the default naming strategy
* for that cloud.
*/
public TemplateOptions nodeNames(Iterable<String> nodeNames) {
this.nodeNames = ImmutableSet.copyOf(checkNotNull(nodeNames, "nodeNames"));
return this;
}
/**
* assigns the created nodes to these security groups
*/
@ -561,6 +594,14 @@ public class TemplateOptions extends RunScriptOptions implements Cloneable {
return options.tags(tags);
}
/**
* @see TemplateOptions#nodeNames
*/
public static TemplateOptions nodeNames(Iterable<String> nodeNames) {
TemplateOptions options = new TemplateOptions();
return options.nodeNames(nodeNames);
}
/**
* @see TemplateOptions#securityGroups
*/

View File

@ -48,10 +48,10 @@ public interface ComputeServiceConstants {
public static final String COMPUTE_LOGGER = "jclouds.compute";
public static final String LOCAL_PARTITION_GB_PATTERN = "disk_drive/%s/gb";
public static final String NODE_GROUP_KEY = "jclouds-group";
@Singleton
public static class NamingConvention {
@Inject(optional = true)
public final Supplier<String> randomSuffix = new Supplier<String>() {
final SecureRandom random = new SecureRandom();

View File

@ -48,6 +48,8 @@ import org.jclouds.compute.strategy.ListNodesStrategy;
import org.jclouds.logging.Logger;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@ -169,9 +171,13 @@ public class CreateNodesWithGroupEncodedIntoNameThenAddToSet implements CreateNo
}
/**
* Find the next node names that can be used. These will be derived from the group and the
* template. We will pre-allocate a specified quantity, and attempt to verify that there is no
* name conflict with the current service.
* Find the next node names that can be used. If the nodeNames template option is not specified
* or is empty, these will be derived from the group and the template. We will pre-allocate a
* specified quantity, and attempt to verify that there is no name conflict with the current
* service. If the nodeNames option is specified, names from that will be used instead, without
* any check for name conflicts.
* If there are insufficient names in nodeNames, subsequent names will be generated in the
* default format.
*
* @param group
* @param count
@ -180,6 +186,12 @@ public class CreateNodesWithGroupEncodedIntoNameThenAddToSet implements CreateNo
*/
protected Set<String> getNextNames(final String group, final Template template, int count) {
Set<String> names = newLinkedHashSet();
Set<String> nodeNames = template.getOptions().getNodeNames();
if (nodeNames.size() >= count) {
return ImmutableSet.copyOf(Iterables.limit(nodeNames, count));
} else {
names.addAll(nodeNames);
}
Iterable<? extends ComputeMetadata> currentNodes = listNodesStrategy.listNodes();
int maxTries = 100;
int currentTries = 0;

View File

@ -40,12 +40,13 @@ import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Processor;
import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.http.HttpRequest;
import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.Statements;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
@ -282,4 +283,12 @@ public class ComputeServiceUtils {
return portRanges;
}
public static String groupFromMapOrName(Map<String, String> metadataMap, String nodeName, GroupNamingConvention namingConvention) {
if (metadataMap.get(ComputeServiceConstants.NODE_GROUP_KEY) != null) {
return metadataMap.get(ComputeServiceConstants.NODE_GROUP_KEY);
} else {
return namingConvention.groupInUniqueNameOrNull(nodeName);
}
}
}

View File

@ -484,6 +484,12 @@ public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTes
super.testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired();
}
@Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired")
public void testCreateTwoNodesWithOneSpecifiedName() throws Exception {
super.testCreateTwoNodesWithOneSpecifiedName();
}
@Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired")
public void testCredentialsCache() throws Exception {
super.testCredentialsCache();

View File

@ -35,6 +35,7 @@ import static java.util.logging.Logger.getAnonymousLogger;
import static org.jclouds.compute.options.RunScriptOptions.Builder.nameTask;
import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript;
import static org.jclouds.compute.options.TemplateOptions.Builder.inboundPorts;
import static org.jclouds.compute.options.TemplateOptions.Builder.nodeNames;
import static org.jclouds.compute.options.TemplateOptions.Builder.overrideLoginCredentials;
import static org.jclouds.compute.options.TemplateOptions.Builder.runAsRoot;
import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
@ -44,6 +45,7 @@ import static org.jclouds.compute.predicates.NodePredicates.runningInGroup;
import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
import static org.jclouds.util.Predicates2.retry;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
@ -362,6 +364,30 @@ public abstract class BaseComputeServiceLiveTest extends BaseComputeServiceConte
checkOsMatchesTemplate(node2);
}
@Test(enabled = true, dependsOnMethods = "testCreateTwoNodesWithRunScript")
public void testCreateTwoNodesWithOneSpecifiedName() throws Exception {
Set<? extends NodeMetadata> nodes;
try {
nodes = newTreeSet(client.createNodesInGroup(group, 2, nodeNames(ImmutableSet.of("first-node"))));
} catch (RunNodesException e) {
nodes = newTreeSet(concat(e.getSuccessfulNodes(), e.getNodeErrors().keySet()));
throw e;
}
assertEquals(nodes.size(), 2, "expected two nodes but was " + nodes);
NodeMetadata node1 = Iterables.getFirst(nodes, null);
NodeMetadata node2 = Iterables.getLast(nodes, null);
// credentials aren't always the same
// assertEquals(node1.getCredentials(), node2.getCredentials());
assertTrue(node1.getName().equals("first-node") || node2.getName().equals("first-node"),
"one node should be named 'first-node'");
assertFalse(node1.getName().equals("first-node") && node2.getName().equals("first-node"),
"one node should be named something other than 'first-node");
this.nodes.addAll(nodes);
}
private Template refreshTemplate() {
return template = addRunScriptToTemplate(buildTemplate(client.templateBuilder()));
}
@ -391,7 +417,7 @@ public abstract class BaseComputeServiceLiveTest extends BaseComputeServiceConte
}
}
@Test(enabled = true, dependsOnMethods = "testCreateTwoNodesWithRunScript")
@Test(enabled = true, dependsOnMethods = "testCreateTwoNodesWithOneSpecifiedName")
public void testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired() throws Exception {
initializeContext();

View File

@ -21,12 +21,17 @@ import static org.jclouds.compute.options.TemplateOptions.Builder.blockOnPort;
import static org.jclouds.compute.options.TemplateOptions.Builder.blockUntilRunning;
import static org.jclouds.compute.options.TemplateOptions.Builder.inboundPorts;
import static org.jclouds.compute.options.TemplateOptions.Builder.installPrivateKey;
import static org.jclouds.compute.options.TemplateOptions.Builder.nodeNames;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.util.Set;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
* Tests possible uses of TemplateOptions and TemplateOptions.Builder.*
*
@ -184,4 +189,11 @@ public class TemplateOptionsTest {
TemplateOptions options = blockUntilRunning(false);
assertEquals(options.shouldBlockUntilRunning(), false);
}
@Test
public void testNodeNames() {
Set<String> nodeNames = ImmutableSet.of("first-node", "second-node");
TemplateOptions options = nodeNames(nodeNames);
assertTrue(options.getNodeNames().containsAll(nodeNames));
}
}

View File

@ -480,6 +480,14 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
AWSEC2TemplateOptions options = new AWSEC2TemplateOptions();
return options.blockUntilRunning(blockUntilRunning);
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static AWSEC2TemplateOptions nodeNames(Iterable<String> nodeNames) {
AWSEC2TemplateOptions options = new AWSEC2TemplateOptions();
return AWSEC2TemplateOptions.class.cast(options.nodeNames(nodeNames));
}
}
// methods that only facilitate returning the correct object type
@ -508,6 +516,14 @@ public class AWSEC2TemplateOptions extends EC2TemplateOptions implements Cloneab
return AWSEC2TemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public AWSEC2TemplateOptions nodeNames(Iterable<String> nodeNames) {
return AWSEC2TemplateOptions.class.cast(super.nodeNames(nodeNames));
}
/**
* {@inheritDoc}
*/

View File

@ -111,6 +111,7 @@ public class GleSYSComputeServiceAdapter implements ComputeServiceAdapter<Server
CreateServerOptions createServerOptions = new CreateServerOptions();
createServerOptions.ip(templateOptions.getIp());
template.getOptions().userMetadata(ComputeServiceConstants.NODE_GROUP_KEY, group);
Map<String, String> md = metadataAndTagsAsCommaDelimitedValue(template.getOptions());
if (md.size() > 0) {

View File

@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.io.BaseEncoding.base16;
import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromCommaDelimitedValue;
import static org.jclouds.compute.util.ComputeServiceUtils.groupFromMapOrName;
import static org.jclouds.location.predicates.LocationPredicates.idEquals;
import java.util.Map;
@ -109,15 +110,18 @@ public class ServerDetailsToNodeMetadata implements Function<ServerDetails, Node
builder.hostname(from.getHostname());
Location location = FluentIterable.from(locations.get()).firstMatch(idEquals(from.getDatacenter())).orNull();
checkState(location != null, "no location matched ServerDetails %s", from);
builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getHostname()));
Map<String, String> metadataMap;
// TODO: get glesys to stop stripping out equals and commas!
if (!isNullOrEmpty(from.getDescription()) && from.getDescription().matches("^[0-9A-Fa-f]+$")) {
String decoded = new String(base16().lowerCase().decode(from.getDescription()), UTF_8);
addMetadataAndParseTagsFromCommaDelimitedValue(builder,
Splitter.on('\n').withKeyValueSeparator("=").split(decoded));
metadataMap = Splitter.on('\n').withKeyValueSeparator("=").split(decoded);
addMetadataAndParseTagsFromCommaDelimitedValue(builder, metadataMap);
} else {
metadataMap = ImmutableMap.of();
}
builder.group(groupFromMapOrName(metadataMap, from.getHostname(), nodeNamingConvention));
builder.imageId(from.getTemplateName() + "");
builder.operatingSystem(parseOperatingSystem(from));
builder.hardware(new HardwareBuilder().ids(from.getId() + "").ram(from.getMemorySizeMB())

View File

@ -209,6 +209,14 @@ public class GleSYSTemplateOptions extends TemplateOptions implements Cloneable
GleSYSTemplateOptions options = new GleSYSTemplateOptions();
return GleSYSTemplateOptions.class.cast(options.userMetadata(key, value));
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static GleSYSTemplateOptions nodeNames(Iterable<String> nodeNames) {
GleSYSTemplateOptions options = new GleSYSTemplateOptions();
return GleSYSTemplateOptions.class.cast(options.nodeNames(nodeNames));
}
}
// methods that only facilitate returning the correct object type
@ -261,6 +269,14 @@ public class GleSYSTemplateOptions extends TemplateOptions implements Cloneable
return GleSYSTemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public GleSYSTemplateOptions nodeNames(Iterable<String> nodeNames) {
return GleSYSTemplateOptions.class.cast(super.nodeNames(nodeNames));
}
@Override
public ToStringHelper string() {
ToStringHelper stringHelper = super.string();

View File

@ -97,6 +97,14 @@ public class GoGridTemplateOptions extends TemplateOptions implements Cloneable
GoGridTemplateOptions options = new GoGridTemplateOptions();
return GoGridTemplateOptions.class.cast(options.userMetadata(key, value));
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static GoGridTemplateOptions nodeNames(Iterable<String> nodeNames) {
GoGridTemplateOptions options = new GoGridTemplateOptions();
return GoGridTemplateOptions.class.cast(options.nodeNames(nodeNames));
}
}
// methods that only facilitate returning the correct object type
@ -148,4 +156,12 @@ public class GoGridTemplateOptions extends TemplateOptions implements Cloneable
public GoGridTemplateOptions userMetadata(String key, String value) {
return GoGridTemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public GoGridTemplateOptions nodeNames(Iterable<String> nodeNames) {
return GoGridTemplateOptions.class.cast(super.nodeNames(nodeNames));
}
}

View File

@ -130,6 +130,14 @@ public class SoftLayerTemplateOptions extends TemplateOptions implements Cloneab
SoftLayerTemplateOptions options = new SoftLayerTemplateOptions();
return SoftLayerTemplateOptions.class.cast(options.userMetadata(key, value));
}
/**
* @see TemplateOptions#nodeNames(Iterable)
*/
public static SoftLayerTemplateOptions nodeNames(Iterable<String> nodeNames) {
SoftLayerTemplateOptions options = new SoftLayerTemplateOptions();
return SoftLayerTemplateOptions.class.cast(options.nodeNames(nodeNames));
}
}
// methods that only facilitate returning the correct object type
@ -181,4 +189,12 @@ public class SoftLayerTemplateOptions extends TemplateOptions implements Cloneab
public SoftLayerTemplateOptions userMetadata(String key, String value) {
return SoftLayerTemplateOptions.class.cast(super.userMetadata(key, value));
}
/**
* {@inheritDoc}
*/
@Override
public SoftLayerTemplateOptions nodeNames(Iterable<String> nodeNames) {
return SoftLayerTemplateOptions.class.cast(super.nodeNames(nodeNames));
}
}