Merge pull request #80 from jsonking/master

Issue 158: Started createNodeWithGroupEncodedIntoNameThenStoreCredentials. Fixed an objectMask and entered rest of metadata
This commit is contained in:
Adrian Cole 2011-09-29 09:37:20 -07:00
commit 1649c15ac7
8 changed files with 404 additions and 24 deletions

View File

@ -54,7 +54,7 @@ public class SoftLayerProviderMetadata extends BaseProviderMetadata {
*/
@Override
public String getName() {
return "//TODO SoftLayer";
return "SoftLayer";
}
/**
@ -62,7 +62,7 @@ public class SoftLayerProviderMetadata extends BaseProviderMetadata {
*/
@Override
public String getIdentityName() {
return "//TODO";
return "API Username";
}
/**
@ -70,7 +70,7 @@ public class SoftLayerProviderMetadata extends BaseProviderMetadata {
*/
@Override
public String getCredentialName() {
return "//TODO";
return "API Key";
}
/**
@ -78,7 +78,7 @@ public class SoftLayerProviderMetadata extends BaseProviderMetadata {
*/
@Override
public URI getHomepage() {
return URI.create("//TODO");
return URI.create("http://www.softlayer.com");
}
/**
@ -86,7 +86,7 @@ public class SoftLayerProviderMetadata extends BaseProviderMetadata {
*/
@Override
public URI getConsole() {
return URI.create("//TODO");
return URI.create("https://manage.softlayer.com");
}
/**
* {@inheritDoc}

View File

@ -19,6 +19,7 @@
package org.jclouds.softlayer.compute.strategy;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
@ -54,7 +55,6 @@ public class SoftLayerComputeServiceAdapter implements
ComputeServiceAdapter<VirtualGuest, Set<ProductItem>, ProductItem, Datacenter> {
public static final String SAN_DESCRIPTION_REGEX=".*GB \\(SAN\\).*";
//TODO: Better to pass this in as a property like virtualGuestPackageName?
private static final Float BOOT_VOLUME_CAPACITY = 100F;
private final SoftLayerClient client;
@ -68,18 +68,82 @@ public class SoftLayerComputeServiceAdapter implements
}
@Override
public VirtualGuest createNodeWithGroupEncodedIntoNameThenStoreCredentials(String tag, String name,
public VirtualGuest createNodeWithGroupEncodedIntoNameThenStoreCredentials(String group, String name,
Template template, Map<String, Credentials> credentialStore) {
VirtualGuest from = null; // from testCancelAndPlaceOrder()
// use name for hostname as possible
// you can ignore group, unless it is valid to set as domain
// get the cpu, mem, datacenter from the template ids
// make sure you store the credentials so that later functions can use them
// credentialStore.put("node#"+ from.getId() + "", new Credentials(from.loginUser (might be root),
// from.password));
return from;
Iterable<VirtualGuest> existing = findVirtualGuests(name,group);
if(!Iterables.isEmpty(existing)) {
throw new IllegalStateException(
"VirtualGuest(s) already exist with hostname:"+name+", group:"+group+". Existing:"+existing);
}
VirtualGuest newGuest = VirtualGuest.builder()
.domain(group)
.hostname(name)
.build();
ProductOrder order = ProductOrder.builder()
.packageId(getProductPackage().getId())
.location(template.getLocation().getId())
.quantity(1)
.useHourlyPricing(true)
.prices(getPrices(template))
.virtualGuest(newGuest)
.build();
client.getVirtualGuestClient().orderVirtualGuest(order);
VirtualGuest result = Iterables.getOnlyElement(findVirtualGuests(name, group));
Credentials credentials = new Credentials(null,null);
// This information is not always available.
OperatingSystem os = result.getOperatingSystem();
if(os!=null) {
Set<Password> passwords = os.getPasswords();
if(passwords.size()>0) {
Password pw = Iterables.get(passwords,0);
credentials = new Credentials(pw.getUsername(),pw.getPassword());
}
}
credentialStore.put("node#"+result.getId(),credentials);
return result;
}
private Iterable<VirtualGuest> findVirtualGuests(String hostname,String domain) {
checkNotNull(hostname,"hostname");
checkNotNull(domain,"domain");
Set<VirtualGuest> result = Sets.newLinkedHashSet();
for( VirtualGuest guest : client.getVirtualGuestClient().listVirtualGuests()) {
if ( guest.getHostname().equals(hostname) && guest.getDomain().equals(domain)) {
result.add(guest);
}
}
return result;
}
private Iterable<ProductItemPrice> getPrices(Template template) {
Set<ProductItemPrice> result = Sets.newLinkedHashSet();
int imageId = Integer.parseInt(template.getImage().getId());
result.add(ProductItemPrice.builder().id(imageId).build());
Iterable<String> hardwareIds = Splitter.on(",").split(template.getHardware().getId());
for(String hardwareId: hardwareIds) {
int id = Integer.parseInt(hardwareId);
result.add(ProductItemPrice.builder().id(id).build());
}
result.addAll(SoftLayerConstants.DEFAULT_VIRTUAL_GUEST_PRICES);
return result;
}
@Override
public Iterable<Set<ProductItem>> listHardwareProfiles() {
ProductPackage productPackage = getProductPackage();

View File

@ -0,0 +1,137 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.softlayer.domain;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Extends the SoftLayer_Software_Component data type to include operating system specific properties.
*
* @author Jason King
* @see <a href=
* "http://sldn.softlayer.com/reference/datatypes/SoftLayer_Software_Component_OperatingSystem"
* />
*/
public class OperatingSystem implements Comparable<OperatingSystem> {
// There are other properties
public static Builder builder() {
return new Builder();
}
public static class Builder {
private int id = -1;
private Set<Password> passwords = Sets.newLinkedHashSet();
public Builder id(int id) {
this.id = id;
return this;
}
public Builder password(Password password) {
this.passwords.add(checkNotNull(password, "password"));
return this;
}
public Builder passwords(Iterable<Password> passwords) {
this.passwords = ImmutableSet.<Password> copyOf(checkNotNull(passwords, "passwords"));
return this;
}
public OperatingSystem build() {
return new OperatingSystem(id, passwords);
}
public static Builder fromOperatingSystem(OperatingSystem in) {
return OperatingSystem.builder()
.id(in.getId())
.passwords(in.getPasswords());
}
}
private int id = -1;
private Set<Password> passwords = Sets.newLinkedHashSet();
// for deserializer
OperatingSystem() {
}
public OperatingSystem(int id,Iterable<Password> passwords) {
this.id = id;
this.passwords = ImmutableSet.<Password> copyOf(checkNotNull(passwords, "passwords"));
}
@Override
public int compareTo(OperatingSystem arg0) {
return new Integer(id).compareTo(arg0.getId());
}
/**
* @return An ID number identifying this Software Component (Software Installation)
*/
public int getId() {
return id;
}
/**
*
* @return Username/Password pairs used for access to this Software Installation.
*/
public Set<Password> getPasswords() {
return passwords;
}
public Builder toBuilder() {
return Builder.fromOperatingSystem(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (id ^ (id >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
OperatingSystem other = (OperatingSystem) obj;
if (id != other.id)
return false;
return true;
}
@Override
public String toString() {
return "OperatingSystem [id=" + id + ", passwords=" + passwords + "]";
}
}

View File

@ -0,0 +1,141 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.softlayer.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.emptyToNull;
/**
*
* Contains a password for a specific software component instance
*
* @author Jason King
* @see <a href= "http://sldn.softlayer.com/reference/datatypes/SoftLayer_Software_Component_Password"
* />
*/
public class Password implements Comparable<Password> {
public static Builder builder() {
return new Builder();
}
public static class Builder {
private int id = -1;
private String username;
private String password;
public Builder id(int id) {
this.id = id;
return this;
}
public Builder username(String username) {
this.username = username;
return this;
}
public Builder password(String password) {
this.password = password;
return this;
}
public Password build() {
return new Password(id, username, password);
}
public static Builder fromPassword(Password in) {
return Password.builder().id(in.getId())
.username(in.getUsername())
.password(in.getPassword());
}
}
private int id = -1;
private String username;
private String password;
// for deserializer
Password() {
}
public Password(int id, String username, String password) {
this.id = id;
this.username = checkNotNull(emptyToNull(username),"username cannot be null or empty:"+username);
this.password = password;
}
@Override
public int compareTo(Password arg0) {
return new Integer(id).compareTo(arg0.getId());
}
/**
* @return An id number for this specific username/password pair.
*/
public int getId() {
return id;
}
/**
* @return The username part of the username/password pair.
*/
public String getUsername() {
return username;
}
/**
* @return The password part of the username/password pair.
*/
public String getPassword() {
return password;
}
public Builder toBuilder() {
return Builder.fromPassword(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (id ^ (id >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Password other = (Password) obj;
if (id != other.id)
return false;
return true;
}
@Override
public String toString() {
return "Password [id=" + id + ", username=" + username + ", password=**********]";
}
}

View File

@ -31,6 +31,17 @@ import com.google.gson.annotations.SerializedName;
* interaction. <br/>
* A guest, also known as a virtual server or CloudLayer Computing Instance, represents an
* allocation of resources on a virtual host.
*
* The hostname and domain must be alphanumeric strings that may be separated by periods '.'.
* The only other allowable special character is the dash '-'.
* However the special characters '.' and '-' may not be consecutive.
* Each alphanumeric string separated by a period is considered a label.
* Labels must begin and end with an alphanumeric character.
* Each label cannot be soley comprised of digits and must be between 1-63 characters in length.
* The last label, the TLD (top level domain) must be between 2-6 alphabetic characters.
* The domain portion must consist of least one label followed by a period '.' then ending with the TLD label.
* Combining the hostname, followed by a period '.', followed by the domain gives the FQDN (fully qualified domain name),
* which may not exceed 253 characters in total length.
*
* @author Adrian Cole
* @see <a href=
@ -64,6 +75,7 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
private String primaryBackendIpAddress;
private String primaryIpAddress;
private BillingItemVirtualGuest billingItem;
private OperatingSystem operatingSystem;
public Builder id(int id) {
this.id = id;
@ -170,12 +182,17 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
return this;
}
public Builder operatingSystem(OperatingSystem operatingSystem) {
this.operatingSystem = operatingSystem;
return this;
}
public VirtualGuest build() {
return new VirtualGuest(accountId, createDate, dedicatedAccountHostOnly, domain,
fullyQualifiedDomainName, hostname, id, lastVerifiedDate, maxCpu,
maxCpuUnits, maxMemory, metricPollDate, modifyDate, notes,
privateNetworkOnly, startCpus, statusId, uuid, primaryBackendIpAddress,
primaryIpAddress,billingItem);
primaryIpAddress,billingItem,operatingSystem);
}
public static Builder fromVirtualGuest(VirtualGuest in) {
@ -200,7 +217,9 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
.uuid(in.getUuid())
.primaryBackendIpAddress(in.getPrimaryBackendIpAddress())
.primaryIpAddress(in.getPrimaryIpAddress())
.billingItem(in.getBillingItem());
.billingItem(in.getBillingItem())
.operatingSystem(in.getOperatingSystem());
}
}
@ -252,6 +271,7 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
private String primaryIpAddress;
private BillingItemVirtualGuest billingItem;
private OperatingSystem operatingSystem;
// for deserializer
VirtualGuest() {
@ -262,7 +282,7 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
String fullyQualifiedDomainName, String hostname, int id, Date lastVerifiedDate, int maxCpu,
String maxCpuUnits, int maxMemory, Date metricPollDate, Date modifyDate, String notes,
boolean privateNetworkOnly, int startCpus, int statusId, String uuid, String primaryBackendIpAddress,
String primaryIpAddress,BillingItemVirtualGuest billingItem) {
String primaryIpAddress,BillingItemVirtualGuest billingItem, OperatingSystem operatingSystem) {
this.accountId = accountId;
this.createDate = createDate;
this.dedicatedAccountHostOnly = dedicatedAccountHostOnly;
@ -284,6 +304,7 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
this.primaryBackendIpAddress = primaryBackendIpAddress;
this.primaryIpAddress = primaryIpAddress;
this.billingItem = billingItem;
this.operatingSystem = operatingSystem;
}
@Override
@ -441,6 +462,13 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
return billingItem;
}
/**
* @return A guest's operating system.
*/
public OperatingSystem getOperatingSystem() {
return operatingSystem;
}
@Override
public int hashCode() {
final int prime = 31;
@ -466,6 +494,7 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
result = prime * result + statusId;
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
result = prime * result + ((billingItem == null) ? 0 : billingItem.hashCode());
result = prime * result + ((operatingSystem == null) ? 0 : operatingSystem.hashCode());
return result;
}
@ -559,6 +588,11 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
return false;
} else if (!billingItem.equals(other.billingItem))
return false;
if (operatingSystem == null) {
if (other.operatingSystem != null)
return false;
} else if (!operatingSystem.equals(other.operatingSystem))
return false;
return true;
}
@ -571,7 +605,7 @@ public class VirtualGuest implements Comparable<VirtualGuest> {
+ ", metricPollDate=" + metricPollDate + ", modifyDate=" + modifyDate + ", notes=" + notes
+ ", primaryBackendIpAddress=" + primaryBackendIpAddress + ", primaryIpAddress=" + primaryIpAddress
+ ", privateNetworkOnly=" + privateNetworkOnly + ", startCpus=" + startCpus + ", statusId=" + statusId
+ ", uuid=" + uuid + ", billingItem="+billingItem+"]";
+ ", uuid=" + uuid + ", billingItem="+billingItem+", operatingSystem="+operatingSystem+"]";
}
}

View File

@ -47,6 +47,7 @@ import java.util.Set;
@RequestFilters(BasicAuthentication.class)
@Path("/v{jclouds.api-version}")
public interface VirtualGuestAsyncClient {
public static String LIST_GUEST_MASK = "virtualGuests.powerState;virtualGuests.networkVlans;virtualGuests.operatingSystem.passwords;virtualGuests.datacenter;virtualGuests.billingItem";
public static String GUEST_MASK = "powerState;networkVlans;operatingSystem.passwords;datacenter;virtualGuests.billingItem";
/**
@ -54,7 +55,7 @@ public interface VirtualGuestAsyncClient {
*/
@GET
@Path("/SoftLayer_Account/VirtualGuests.json")
@QueryParams(keys = "objectMask", values = GUEST_MASK)
@QueryParams(keys = "objectMask", values = LIST_GUEST_MASK)
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
ListenableFuture<Set<VirtualGuest>> listVirtualGuests();

View File

@ -64,9 +64,12 @@ public class SoftLayerComputeServiceAdapterLiveTest extends BaseSoftLayerClientL
@Test
public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentials() {
String group = "foo";
String group = "jclouds.org";
String name = "foo-ef4";
Template template = computeContext.getComputeService().templateBuilder().build();
Template template = computeContext.getComputeService().templateBuilder()
.locationId("3") // the default (singapore) doesn't work.
.build();
Map<String, Credentials> credentialStore = Maps.newLinkedHashMap();
guest = adapter.createNodeWithGroupEncodedIntoNameThenStoreCredentials(group, name, template, credentialStore);
assertEquals(guest.getHostname(), name);

View File

@ -47,7 +47,7 @@ public class VirtualGuestAsyncClientTest extends BaseSoftLayerAsyncClientTest<Vi
assertRequestLineEquals(
httpRequest,
"GET https://api.softlayer.com/rest/v3/SoftLayer_Account/VirtualGuests.json?objectMask=powerState%3BnetworkVlans%3BoperatingSystem.passwords%3Bdatacenter%3BvirtualGuests.billingItem HTTP/1.1");
"GET https://api.softlayer.com/rest/v3/SoftLayer_Account/VirtualGuests.json?objectMask=virtualGuests.powerState%3BvirtualGuests.networkVlans%3BvirtualGuests.operatingSystem.passwords%3BvirtualGuests.datacenter%3BvirtualGuests.billingItem HTTP/1.1");
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null, null, false);
@ -57,7 +57,7 @@ public class VirtualGuestAsyncClientTest extends BaseSoftLayerAsyncClientTest<Vi
assertRequestLineEquals(
httpRequest,
"GET https://api.softlayer.com/rest/v3/SoftLayer_Account/VirtualGuests.json?objectMask=powerState%3BnetworkVlans%3BoperatingSystem.passwords%3Bdatacenter%3BvirtualGuests.billingItem HTTP/1.1");
"GET https://api.softlayer.com/rest/v3/SoftLayer_Account/VirtualGuests.json?objectMask=virtualGuests.powerState%3BvirtualGuests.networkVlans%3BvirtualGuests.operatingSystem.passwords%3BvirtualGuests.datacenter%3BvirtualGuests.billingItem HTTP/1.1");
// for example, using basic authentication, we should get "only one"
// header
assertNonPayloadHeadersEqual(httpRequest,