From c24ba815cf285a803afe6fa252f7cf208b92c868 Mon Sep 17 00:00:00 2001 From: Jason King Date: Wed, 28 Sep 2011 16:58:08 +0100 Subject: [PATCH 1/5] Issue 158: Fixed objectMask for VirtualGuest query --- .../jclouds/softlayer/features/VirtualGuestAsyncClient.java | 3 ++- .../softlayer/features/VirtualGuestAsyncClientTest.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/features/VirtualGuestAsyncClient.java b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/features/VirtualGuestAsyncClient.java index b70b464ea5..7895ecf0d2 100644 --- a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/features/VirtualGuestAsyncClient.java +++ b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/features/VirtualGuestAsyncClient.java @@ -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> listVirtualGuests(); diff --git a/sandbox-providers/softlayer/src/test/java/org/jclouds/softlayer/features/VirtualGuestAsyncClientTest.java b/sandbox-providers/softlayer/src/test/java/org/jclouds/softlayer/features/VirtualGuestAsyncClientTest.java index 45c5e33901..0c88c0f22a 100644 --- a/sandbox-providers/softlayer/src/test/java/org/jclouds/softlayer/features/VirtualGuestAsyncClientTest.java +++ b/sandbox-providers/softlayer/src/test/java/org/jclouds/softlayer/features/VirtualGuestAsyncClientTest.java @@ -47,7 +47,7 @@ public class VirtualGuestAsyncClientTest extends BaseSoftLayerAsyncClientTest Date: Wed, 28 Sep 2011 16:59:09 +0100 Subject: [PATCH 2/5] Issue 158: Added operating system and password to virtual guest --- .../softlayer/domain/OperatingSystem.java | 137 +++++++++++++++++ .../jclouds/softlayer/domain/Password.java | 141 ++++++++++++++++++ .../softlayer/domain/VirtualGuest.java | 31 +++- 3 files changed, 305 insertions(+), 4 deletions(-) create mode 100644 sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/OperatingSystem.java create mode 100644 sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/Password.java diff --git a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/OperatingSystem.java b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/OperatingSystem.java new file mode 100644 index 0000000000..053c740e36 --- /dev/null +++ b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/OperatingSystem.java @@ -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 + */ +public class OperatingSystem implements Comparable { + + // There are other properties + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private int id = -1; + private Set 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 passwords) { + this.passwords = ImmutableSet. 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 passwords = Sets.newLinkedHashSet(); + + // for deserializer + OperatingSystem() { + + } + + public OperatingSystem(int id,Iterable passwords) { + this.id = id; + this.passwords = ImmutableSet. 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 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 + "]"; + } +} diff --git a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/Password.java b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/Password.java new file mode 100644 index 0000000000..bb28d29574 --- /dev/null +++ b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/Password.java @@ -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 + */ +public class Password implements Comparable { + 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=**********]"; + } + + +} diff --git a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/VirtualGuest.java b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/VirtualGuest.java index 05855c761b..fb5b98253d 100644 --- a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/VirtualGuest.java +++ b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/VirtualGuest.java @@ -64,6 +64,7 @@ public class VirtualGuest implements Comparable { private String primaryBackendIpAddress; private String primaryIpAddress; private BillingItemVirtualGuest billingItem; + private OperatingSystem operatingSystem; public Builder id(int id) { this.id = id; @@ -170,12 +171,17 @@ public class VirtualGuest implements Comparable { 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 +206,9 @@ public class VirtualGuest implements Comparable { .uuid(in.getUuid()) .primaryBackendIpAddress(in.getPrimaryBackendIpAddress()) .primaryIpAddress(in.getPrimaryIpAddress()) - .billingItem(in.getBillingItem()); + .billingItem(in.getBillingItem()) + .operatingSystem(in.getOperatingSystem()); + } } @@ -252,6 +260,7 @@ public class VirtualGuest implements Comparable { private String primaryIpAddress; private BillingItemVirtualGuest billingItem; + private OperatingSystem operatingSystem; // for deserializer VirtualGuest() { @@ -262,7 +271,7 @@ public class VirtualGuest implements Comparable { 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 +293,7 @@ public class VirtualGuest implements Comparable { this.primaryBackendIpAddress = primaryBackendIpAddress; this.primaryIpAddress = primaryIpAddress; this.billingItem = billingItem; + this.operatingSystem = operatingSystem; } @Override @@ -441,6 +451,13 @@ public class VirtualGuest implements Comparable { return billingItem; } + /** + * @return A guest's operating system. + */ + public OperatingSystem getOperatingSystem() { + return operatingSystem; + } + @Override public int hashCode() { final int prime = 31; @@ -466,6 +483,7 @@ public class VirtualGuest implements Comparable { 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 +577,11 @@ public class VirtualGuest implements Comparable { 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 +594,7 @@ public class VirtualGuest implements Comparable { + ", 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+"]"; } } From b77bd0e132cac89f3656475821af1851f29f6a13 Mon Sep 17 00:00:00 2001 From: Jason King Date: Wed, 28 Sep 2011 17:08:14 +0100 Subject: [PATCH 3/5] Issue 158: Remaining metadata --- .../jclouds/softlayer/SoftLayerProviderMetadata.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerProviderMetadata.java b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerProviderMetadata.java index abf0a02c56..7afeec1a51 100644 --- a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerProviderMetadata.java +++ b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerProviderMetadata.java @@ -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} From 5122e2de453b585671cfcb12792c4986a0597976 Mon Sep 17 00:00:00 2001 From: Jason King Date: Wed, 28 Sep 2011 17:09:53 +0100 Subject: [PATCH 4/5] Issue 158: Started implementation of createNodeWithGroupEncodedIntoNameThenStoreCredentials --- .../SoftLayerComputeServiceAdapter.java | 84 ++++++++++++++++--- ...oftLayerComputeServiceAdapterLiveTest.java | 7 +- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/compute/strategy/SoftLayerComputeServiceAdapter.java b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/compute/strategy/SoftLayerComputeServiceAdapter.java index 37a1de5f0b..398f4befb3 100644 --- a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/compute/strategy/SoftLayerComputeServiceAdapter.java +++ b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/compute/strategy/SoftLayerComputeServiceAdapter.java @@ -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, 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 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 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 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 findVirtualGuests(String hostname,String domain) { + checkNotNull(hostname,"hostname"); + checkNotNull(domain,"domain"); + + Set 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 getPrices(Template template) { + Set result = Sets.newLinkedHashSet(); + + int imageId = Integer.parseInt(template.getImage().getId()); + result.add(ProductItemPrice.builder().id(imageId).build()); + + Iterable 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> listHardwareProfiles() { ProductPackage productPackage = getProductPackage(); diff --git a/sandbox-providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerComputeServiceAdapterLiveTest.java b/sandbox-providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerComputeServiceAdapterLiveTest.java index f13d1e0f18..1687a52ba5 100644 --- a/sandbox-providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerComputeServiceAdapterLiveTest.java +++ b/sandbox-providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerComputeServiceAdapterLiveTest.java @@ -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 credentialStore = Maps.newLinkedHashMap(); guest = adapter.createNodeWithGroupEncodedIntoNameThenStoreCredentials(group, name, template, credentialStore); assertEquals(guest.getHostname(), name); From dd189cf6f0f00d9a80c707467633809b30a4bcde Mon Sep 17 00:00:00 2001 From: Jason King Date: Thu, 29 Sep 2011 10:11:42 +0100 Subject: [PATCH 5/5] Issue 158: Added comment about hostname/domain constraints --- .../org/jclouds/softlayer/domain/VirtualGuest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/VirtualGuest.java b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/VirtualGuest.java index fb5b98253d..e1a1f2711f 100644 --- a/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/VirtualGuest.java +++ b/sandbox-providers/softlayer/src/main/java/org/jclouds/softlayer/domain/VirtualGuest.java @@ -31,6 +31,17 @@ import com.google.gson.annotations.SerializedName; * interaction.
* 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