You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2014/05/26 17:12:37 UTC

[3/3] git commit: JCLOUDS-517: New ElasticHosts images and regions

JCLOUDS-517: New ElasticHosts images and regions

Added the new ElasticHosts regions.

Updated the ElasticStack api to get the list of standard
drives using an API call. All providers except ServerLove
support the new API call, so the old logic in the ElasticStack
api has been moved to that provider. The rest of providers will now
extract all the OperatingSystem information by parsing the name of the
StandardDrive.

A unit test has been added to the ElasticStack api with all the images
that were hardcoded, to make sure all names are still parsed as expected
and all information in the existing providers is kept.

Modified the default template for all ElasticHosts providers to
match newer Ubuntu images and updated the Template*Live tests
accordingly.

Also refactored the WellKnownImage map to a supplier to lazy load it
when needed and avoid unexpected errors when building the Guice injector
if there are authentication errors or similar.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/fbf30a73
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/fbf30a73
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/fbf30a73

Branch: refs/heads/1.7.x
Commit: fbf30a73a77226a16bac5cfc8fcd67167f9b7395
Parents: eb000b2
Author: Ignasi Barrera <na...@apache.org>
Authored: Tue Mar 25 15:00:07 2014 +0100
Committer: Ignasi Barrera <na...@apache.org>
Committed: Mon May 26 16:39:08 2014 +0200

----------------------------------------------------------------------
 apis/elasticstack/pom.xml                       |  12 +
 .../jclouds/elasticstack/ElasticStackApi.java   |  22 ++
 .../elasticstack/ElasticStackApiMetadata.java   |   1 -
 .../ElasticStackComputeServiceAdapter.java      |  28 +-
 ...ElasticStackComputeServiceContextModule.java |  58 +++-
 .../StandardDriveToWellKnownImage.java          | 131 +++++++++
 .../functions/WellKnownImageToImage.java        |  18 +-
 .../elasticstack/domain/StandardDrive.java      | 185 +++++++++++++
 .../elasticstack/domain/WellKnownImage.java     |  88 +++++-
 ...DelimitedByBlankLinesToStandardDriveSet.java |  60 ++++
 .../functions/MapToStandardDrive.java           |  78 ++++++
 .../elasticstack/functions/SplitNewlines.java   |   7 +-
 .../handlers/ElasticStackErrorHandler.java      |  17 +-
 .../suppliers/StandardDiskImageSupplier.java    |  64 +++++
 .../suppliers/WellKnownImageSupplier.java       |  35 +++
 .../elasticstack/preinstalled_images.json       |  72 -----
 .../main/resources/preinstalled_images.readme   |   7 -
 .../elasticstack/ElasticStackApiLiveTest.java   |  16 ++
 .../elasticstack/ElasticStackApiTest.java       |  15 +
 .../elasticstack/ElasticStackMockTest.java      | 110 ++++++++
 .../StandardDriveToWellKnownImageTest.java      | 121 ++++++++
 ...mitedByBlankLinesToStandardDriveSetTest.java |  47 ++++
 .../functions/MapToStandardDriveTest.java       |  68 +++++
 .../MockStandardDiskImageSupplier.java          |  69 +++++
 .../src/test/resources/standard_drive.txt       |   8 +
 .../src/test/resources/standard_drives.txt      | 275 +++++++++++++++++++
 .../test/resources/standard_drives_uuids.txt    |  36 +++
 providers/elastichosts-ams-e/README.txt         |   6 +
 providers/elastichosts-ams-e/pom.xml            | 125 +++++++++
 .../ElasticHostsAmsterdamMetadata.java          |  86 ++++++
 .../org.jclouds.providers.ProviderMetadata      |   1 +
 .../ElasticHostsAmsterdamApiLiveTest.java       |  30 ++
 .../ElasticHostsAmsterdamProviderTest.java      |  32 +++
 ...ticHostsAmsterdamComputeServiceLiveTest.java |  33 +++
 ...icHostsAmsterdamTemplateBuilderLiveTest.java |  85 ++++++
 providers/elastichosts-hkg-e/README.txt         |   5 +
 providers/elastichosts-hkg-e/pom.xml            | 125 +++++++++
 .../ElasticHostsHongKongProviderMetadata.java   |  86 ++++++
 .../org.jclouds.providers.ProviderMetadata      |   1 +
 .../ElasticHostsHongKongApiLiveTest.java        |  30 ++
 .../ElasticHostsHongKongProviderTest.java       |  32 +++
 ...sticHostsHongKongComputeServiceLiveTest.java |  33 +++
 ...ticHostsHongKongTemplateBuilderLiveTest.java |  85 ++++++
 ...ticHostsPeer1LosAngelesProviderMetadata.java |   3 +
 .../elastichosts-lax-p/preinstalled_images.json |  65 -----
 ...sPeer1LosAngelesTemplateBuilderLiveTest.java |  25 +-
 ...icHostsBlueSquareLondonProviderMetadata.java |   3 +
 .../elastichosts-lon-b/preinstalled_images.json |  65 -----
 ...BlueSquareLondonTemplateBuilderLiveTest.java |  31 ++-
 ...ElasticHostsPeer1LondonProviderMetadata.java |   3 +
 .../elastichosts-lon-p/preinstalled_images.json |  65 -----
 ...HostsPeer1LondonTemplateBuilderLiveTest.java |  26 +-
 ...ticHostsPeer1SanAntonioProviderMetadata.java |   3 +
 .../elastichosts-sat-p/preinstalled_images.json |  72 -----
 ...sPeer1SanAntonioTemplateBuilderLiveTest.java |  25 +-
 providers/elastichosts-sjc-c/README.txt         |   5 +
 providers/elastichosts-sjc-c/pom.xml            | 125 +++++++++
 .../ElasticHostsSanJoseProviderMetadata.java    |  83 ++++++
 .../org.jclouds.providers.ProviderMetadata      |   1 +
 .../ElasticHostsSanJoseApiLiveTest.java         |  30 ++
 .../ElasticHostsSanJoseProviderTest.java        |  32 +++
 ...asticHostsSanJoseComputeServiceLiveTest.java |  33 +++
 ...sticHostsSanJoseTemplateBuilderLiveTest.java |  85 ++++++
 providers/elastichosts-syd-v/README.txt         |   5 +
 providers/elastichosts-syd-v/pom.xml            | 125 +++++++++
 .../ElasticHostsSydneyProviderMetadata.java     |  83 ++++++
 .../org.jclouds.providers.ProviderMetadata      |   1 +
 .../ElasticHostsSydneyApiLiveTest.java          |  30 ++
 .../ElasticHostsSydneyProviderTest.java         |  32 +++
 ...lasticHostsSydneyComputeServiceLiveTest.java |  33 +++
 ...asticHostsSydneyTemplateBuilderLiveTest.java |  85 ++++++
 ...lasticHostsPeer1TorontoProviderMetadata.java |   3 +
 .../elastichosts-tor-p/preinstalled_images.json |  65 -----
 ...ostsPeer1TorontoTemplateBuilderLiveTest.java |  25 +-
 .../go2cloud-jhb1/preinstalled_images.json      |  30 --
 .../openhosting-east1/preinstalled_images.json  |  51 ----
 providers/pom.xml                               |   4 +
 .../ServerloveManchesterProviderMetadata.java   |  32 ++-
 .../config/ServerloveImagesModule.java          |  38 +++
 .../suppliers/FixedWellKnownImageSupplier.java  |  67 +++++
 .../ServerloveManchesterApiLiveTest.java        |  13 +
 .../skalicloud-sdg-my/preinstalled_images.json  |  98 -------
 82 files changed, 3324 insertions(+), 719 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/pom.xml
----------------------------------------------------------------------
diff --git a/apis/elasticstack/pom.xml b/apis/elasticstack/pom.xml
index 8b37358..ddf599f 100644
--- a/apis/elasticstack/pom.xml
+++ b/apis/elasticstack/pom.xml
@@ -78,6 +78,18 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp</groupId>
+      <artifactId>mockwebserver</artifactId>
+      <scope>test</scope>
+      <exclusions>
+        <!-- Already provided by jclouds-sshj -->
+        <exclusion>
+          <groupId>org.bouncycastle</groupId>
+          <artifactId>bcprov-jdk15on</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
   </dependencies>
   <profiles>
     <profile>

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java
index bc6fe39..7e7b056 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java
@@ -38,10 +38,12 @@ import org.jclouds.elasticstack.domain.DriveInfo;
 import org.jclouds.elasticstack.domain.ImageConversionType;
 import org.jclouds.elasticstack.domain.Server;
 import org.jclouds.elasticstack.domain.ServerInfo;
+import org.jclouds.elasticstack.domain.StandardDrive;
 import org.jclouds.elasticstack.functions.KeyValuesDelimitedByBlankLinesToDriveInfo;
 import org.jclouds.elasticstack.functions.KeyValuesDelimitedByBlankLinesToServerInfo;
 import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet;
 import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet;
+import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet;
 import org.jclouds.elasticstack.functions.ReturnPayload;
 import org.jclouds.elasticstack.functions.SplitNewlines;
 import org.jclouds.http.filters.BasicAuthentication;
@@ -201,6 +203,26 @@ public interface ElasticStackApi extends Closeable {
    @Path("/drives/info")
    @ResponseParser(ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet.class)
    Set<DriveInfo> listDriveInfo();
+   
+   /**
+    * Lists standard drive UUIDs in your account
+    *
+    * @return or empty set if no standard drives are found
+    */
+   @GET
+   @Path("/drives/list/standard")
+   @ResponseParser(SplitNewlines.class)
+   Set<String> listStandardDrives();
+
+   /**
+    * Gets information about all standard drives
+    *
+    * @return or empty set if no standard drives are found
+    */
+   @GET
+   @Path("/drives/info/standard")
+   @ResponseParser(ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet.class)
+   Set<StandardDrive> listStandardDriveInfo();
 
    /**
     * @param uuid

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
index 9625beb..19c6309 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
@@ -62,7 +62,6 @@ public class ElasticStackApiMetadata extends BaseHttpApiMetadata<ElasticStackApi
 
    public static class Builder extends BaseHttpApiMetadata.Builder<ElasticStackApi, Builder> {
 
-      @SuppressWarnings("deprecation")
       protected Builder() {
          id("elasticstack")
          .name("ElasticStack API")

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java
index a478cf4..e1364d0 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java
@@ -32,6 +32,7 @@ import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.jclouds.Constants;
+import org.jclouds.collect.Memoized;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.HardwareBuilder;
@@ -57,6 +58,7 @@ import org.jclouds.logging.Logger;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.FluentIterable;
@@ -70,7 +72,7 @@ import com.google.common.util.concurrent.UncheckedExecutionException;
 
 /**
  * defines the connection between the {@link org.jclouds.elasticstack.ElasticStackApi} implementation
- * and the jclouds {@link ComputeService}
+ * and the jclouds {@link org.jclouds.compute.ComputeService}
  * 
  */
 @Singleton
@@ -78,7 +80,7 @@ public class ElasticStackComputeServiceAdapter implements
       ComputeServiceAdapter<ServerInfo, Hardware, DriveInfo, Location> {
    private final ElasticStackApi client;
    private final Predicate<DriveInfo> driveNotClaimed;
-   private final Map<String, WellKnownImage> preinstalledImages;
+   private final Supplier<Map<String, WellKnownImage>> preinstalledImages;
    private final LoadingCache<String, DriveInfo> cache;
    private final String defaultVncPassword;
    private final ListeningExecutorService userExecutor;
@@ -89,7 +91,7 @@ public class ElasticStackComputeServiceAdapter implements
 
    @Inject
    public ElasticStackComputeServiceAdapter(ElasticStackApi client, Predicate<DriveInfo> driveNotClaimed,
-         Map<String, WellKnownImage> preinstalledImages, LoadingCache<String, DriveInfo> cache,
+         @Memoized Supplier<Map<String, WellKnownImage>> preinstalledImages, LoadingCache<String, DriveInfo> cache,
          @Named(ElasticStackConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword,
          @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor) {
       this.client = checkNotNull(client, "client");
@@ -108,16 +110,22 @@ public class ElasticStackComputeServiceAdapter implements
       logger.debug(">> creating boot drive bytes(%d)", bootSize);
       DriveInfo drive = client
             .createDrive(new Drive.Builder().name(template.getImage().getId()).size(bootSize).build());
-      logger.debug("<< drive(%s)", drive.getUuid());
+      logger.debug("<< drive (%s)", drive);
 
-      logger.debug(">> imaging boot drive source(%s)", template.getImage().getId());
-      client.imageDrive(template.getImage().getId(), drive.getUuid(), ImageConversionType.GUNZIP);
       boolean success = driveNotClaimed.apply(drive);
-      logger.debug("<< imaged (%s)", success);
       if (!success) {
          client.destroyDrive(drive.getUuid());
-         throw new IllegalStateException("could not image drive in time!");
+         throw new IllegalStateException(String.format("could not create drive %s in time!", drive));
+      }
+
+      logger.debug(">> imaging boot drive source(%s)", template.getImage().getId());
+      client.imageDrive(template.getImage().getId(), drive.getUuid(), ImageConversionType.GUNZIP);
+      boolean ready = driveNotClaimed.apply(drive);
+      if (!ready) {
+         client.destroyDrive(drive.getUuid());
+         throw new IllegalStateException(String.format("could not image drive %s in time!", drive));
       }
+      logger.debug("<< imaged (%s)", drive);
 
       template.getOptions().userMetadata(ComputeServiceConstants.NODE_GROUP_KEY, tag);
 
@@ -126,6 +134,7 @@ public class ElasticStackComputeServiceAdapter implements
                .tags(template.getOptions().getTags()).userMetadata(template.getOptions().getUserMetadata()).build();
 
       ServerInfo from = client.createServer(toCreate);
+
       client.startServer(from.getUuid());
       from = client.getServerInfo(from.getUuid());
       return new NodeAndInitialCredentials<ServerInfo>(from, from.getUuid(), LoginCredentials.builder()
@@ -164,7 +173,7 @@ public class ElasticStackComputeServiceAdapter implements
     */
    @Override
    public Iterable<DriveInfo> listImages() {
-      return FluentIterable.from(transformParallel(preinstalledImages.keySet(),
+      return FluentIterable.from(transformParallel(preinstalledImages.get().keySet(),
             new Function<String, ListenableFuture<? extends DriveInfo>>() {
 
                @Override
@@ -187,7 +196,6 @@ public class ElasticStackComputeServiceAdapter implements
             }, userExecutor, null, logger, "drives")).filter(notNull());
    }
 
-   @SuppressWarnings("unchecked")
    @Override
    public Iterable<ServerInfo> listNodes() {
       return (Iterable<ServerInfo>) client.listServerInfo();

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java
index 4aba9af..9fd590a 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java
@@ -16,16 +16,22 @@
  */
 package org.jclouds.elasticstack.compute.config;
 
+import static com.google.common.base.Suppliers.memoizeWithExpiration;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
 import static org.jclouds.util.Predicates2.retry;
 
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Singleton;
 
+import org.jclouds.collect.Memoized;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
 import org.jclouds.compute.domain.Hardware;
@@ -39,21 +45,24 @@ import org.jclouds.elasticstack.compute.ElasticStackComputeServiceAdapter;
 import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata;
 import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.DeviceToVolume;
 import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.GetImageIdFromServer;
+import org.jclouds.elasticstack.compute.functions.StandardDriveToWellKnownImage;
 import org.jclouds.elasticstack.compute.functions.WellKnownImageToImage;
 import org.jclouds.elasticstack.domain.Device;
 import org.jclouds.elasticstack.domain.DriveInfo;
 import org.jclouds.elasticstack.domain.Server;
 import org.jclouds.elasticstack.domain.ServerInfo;
+import org.jclouds.elasticstack.domain.StandardDrive;
 import org.jclouds.elasticstack.domain.WellKnownImage;
 import org.jclouds.elasticstack.predicates.DriveClaimed;
+import org.jclouds.elasticstack.suppliers.WellKnownImageSupplier;
 import org.jclouds.functions.IdentityFunction;
-import org.jclouds.json.Json;
-import org.jclouds.location.Provider;
-import org.jclouds.util.Strings2;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
@@ -66,7 +75,7 @@ import com.google.inject.TypeLiteral;
  * @author Adrian Cole
  */
 public class ElasticStackComputeServiceContextModule extends
-         ComputeServiceAdapterContextModule<ServerInfo, Hardware, DriveInfo, Location> {
+      ComputeServiceAdapterContextModule<ServerInfo, Hardware, DriveInfo, Location> {
 
    @SuppressWarnings("unchecked")
    @Override
@@ -88,8 +97,12 @@ public class ElasticStackComputeServiceContextModule extends
       }).to(GetImageIdFromServer.class);
       bind(new TypeLiteral<Function<DriveInfo, Image>>() {
       }).to(WellKnownImageToImage.class);
+      bind(new TypeLiteral<Function<StandardDrive, WellKnownImage>>() {
+      }).to(StandardDriveToWellKnownImage.class);
+      bind(new TypeLiteral<Supplier<List<WellKnownImage>>>() {
+      }).to(WellKnownImageSupplier.class);
    }
-   
+
    @Provides
    @Singleton
    protected LoadingCache<String, DriveInfo> cache(GetDrive getDrive) {
@@ -113,18 +126,33 @@ public class ElasticStackComputeServiceContextModule extends
 
    @Singleton
    @Provides
-   protected Map<String, WellKnownImage> provideImages(Json json, @Provider String providerName) throws IOException {
-      List<WellKnownImage> wellKnowns = json.fromJson(Strings2.toStringAndClose(getClass().getResourceAsStream(
-               "/" + providerName + "/preinstalled_images.json")), new TypeLiteral<List<WellKnownImage>>() {
-      }.getType());
-      return Maps.uniqueIndex(wellKnowns, new Function<WellKnownImage, String>() {
-
+   @Memoized
+   protected Supplier<Map<String, WellKnownImage>> provideImages(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
+         @Memoized final Supplier<List<WellKnownImage>> wellKnownImageSupplier) throws IOException {
+      // The image map won't change. Memoize it during the session.
+      // This map can't be created directly as a singleton, as Guice needs it to construct the ElasticStackComputeServiceAdapter
+      // and a misconfiguration such as invalid credentials, etc would cause the Guice injection to fail
+      return memoizeWithExpiration(new Supplier<Map<String, WellKnownImage>>() {
          @Override
-         public String apply(WellKnownImage input) {
-            return input.getUuid();
+         public Map<String, WellKnownImage> get() {
+            return Maps.uniqueIndex(wellKnownImageSupplier.get(), new Function<WellKnownImage, String>() {
+               @Override
+               public String apply(WellKnownImage input) {
+                  return input.getUuid();
+               }
+            });
          }
-
-      });
+      }, seconds, TimeUnit.SECONDS);
+   }
+   
+   @Singleton
+   @Provides
+   @Memoized
+   protected Supplier<List<WellKnownImage>> provideWellKnownImageSupplier(AtomicReference<AuthorizationException> authException,
+         @Named(PROPERTY_SESSION_INTERVAL) long seconds, WellKnownImageSupplier uncached)
+         throws IOException {
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+            TimeUnit.SECONDS);
    }
 
    @Provides

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/StandardDriveToWellKnownImage.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/StandardDriveToWellKnownImage.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/StandardDriveToWellKnownImage.java
new file mode 100644
index 0000000..3480b30
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/StandardDriveToWellKnownImage.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.compute.functions;
+
+import static com.google.common.collect.Iterables.tryFind;
+import static java.util.Arrays.asList;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.elasticstack.domain.WellKnownImage;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+
+/**
+ * Transforms a standard drive into an image that can be used to create nodes.
+ * 
+ * @author Ignasi Barrera
+ */
+@Singleton
+public class StandardDriveToWellKnownImage implements Function<StandardDrive, WellKnownImage> {
+
+   /*
+    * Expression to finds the version in a text string in a Unix OS:
+    * CentOS 6 => 6
+    * CentOS Linux 6.5 =>
+    * Debian Linux 7.4 (Wheezy) => 7.4
+    * Ubuntu Linux 12.04.1 LTS (Precise Pangolin) => 12.04.1
+    */
+   private static final Pattern UNIX_VERSION_PATTERN = Pattern.compile("[^\\d]*(\\d+(?:\\.\\d+)*).*");
+
+   /*
+    * Expression to finds the version in a text string in a Windows OS:
+    * Windows Server 2012 => 2012
+    * Windows Standard 2008 R2 => 2008 R2
+    * Windows Standard 2008 R2 + SQL => 2008 R2 + SQL
+    */
+   private static final Pattern WINDOWS_VERSION_PATTERN = Pattern.compile("[^\\d]*(\\d+.*)");
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   @Override
+   public WellKnownImage apply(StandardDrive input) {
+      WellKnownImage.Builder builder = WellKnownImage.builder();
+      builder.uuid(input.getUuid());
+      builder.size(toGb(input.getSize()));
+      builder.description(input.getName());
+
+      OsFamily family = extractOsFamily(input.getName());
+      String version = extractOsVersion(family, input.getName());
+
+      builder.osFamily(family);
+      builder.osVersion(version);
+      builder.is64bit(is64bit(input.getName()));
+
+      return builder.build();
+   }
+
+   private static boolean is64bit(String name) {
+      return !name.contains("32bit");
+   }
+
+   private OsFamily extractOsFamily(final String name) {
+      final String lowerCaseName = name.toLowerCase();
+      Optional<OsFamily> family = tryFind(asList(OsFamily.values()), new Predicate<OsFamily>() {
+         @Override
+         public boolean apply(OsFamily input) {
+            return lowerCaseName.startsWith(input.name().toLowerCase());
+         }
+      });
+
+      if (family.isPresent()) {
+         logger.warn("could not find the operating system family for image: %s", name);
+      }
+
+      return family.or(OsFamily.UNRECOGNIZED);
+   }
+
+   private String extractOsVersion(OsFamily family, String name) {
+      String version = null;
+      if (family == OsFamily.WINDOWS) {
+         // TODO: Find a way to restrict better the windows version
+         Matcher matcher = WINDOWS_VERSION_PATTERN.matcher(name);
+         if (matcher.matches()) {
+            version = matcher.group(1);
+         }
+      } else {
+         Matcher matcher = UNIX_VERSION_PATTERN.matcher(name);
+         if (matcher.matches()) {
+            version = matcher.group(1);
+         }
+      }
+
+      if (version == null) {
+         logger.warn("could not find the operating system version for image: %s", name);
+      }
+
+      return version;
+   }
+
+   private static int toGb(long sizeInBytes) {
+      return (int) (sizeInBytes / (1024 * 1024 * 1024));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/WellKnownImageToImage.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/WellKnownImageToImage.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/WellKnownImageToImage.java
index 1bbfb9c..179ae28 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/WellKnownImageToImage.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/functions/WellKnownImageToImage.java
@@ -16,15 +16,18 @@
  */
 package org.jclouds.elasticstack.compute.functions;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.Map;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import org.jclouds.collect.Memoized;
 import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.Image.Status;
 import org.jclouds.compute.domain.ImageBuilder;
 import org.jclouds.compute.domain.OperatingSystem;
-import org.jclouds.compute.domain.Image.Status;
 import org.jclouds.domain.Credentials;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LoginCredentials;
@@ -41,19 +44,20 @@ import com.google.common.collect.ImmutableMap;
 @Singleton
 public class WellKnownImageToImage implements Function<DriveInfo, Image> {
    private final Supplier<Location> locationSupplier;
-   private final Map<String, WellKnownImage> preinstalledImages;
+   private final Supplier<Map<String, WellKnownImage>> preinstalledImages;
    private final Map<String, Credentials> credentialStore;
 
    @Inject
-   public WellKnownImageToImage(Supplier<Location> locationSupplier, Map<String, WellKnownImage> preinstalledImages, Map<String, Credentials> credentialStore) {
-      this.locationSupplier = locationSupplier;
-      this.preinstalledImages = preinstalledImages;
-      this.credentialStore = credentialStore;
+   public WellKnownImageToImage(Supplier<Location> locationSupplier,
+         @Memoized Supplier<Map<String, WellKnownImage>> preinstalledImages, Map<String, Credentials> credentialStore) {
+      this.locationSupplier = checkNotNull(locationSupplier, "locationSupplier cannot be null");
+      this.preinstalledImages = checkNotNull(preinstalledImages, "preinstalledImages cannot be null");
+      this.credentialStore = checkNotNull(credentialStore, "credentialStore cannot be null");
    }
 
    @Override
    public Image apply(DriveInfo drive) {
-      WellKnownImage input = preinstalledImages.get(drive.getUuid());
+      WellKnownImage input = preinstalledImages.get().get(drive.getUuid());
       // set credentials in the store here, as opposed to directly modifying the image. we need to
       // set credentials on the image outside of this function so that they can be for example
       // overridden by properties

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/StandardDrive.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/StandardDrive.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/StandardDrive.java
new file mode 100644
index 0000000..3a86701
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/StandardDrive.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * @author Ignasi Barrera
+ */
+public class StandardDrive extends Drive {
+   public static class Builder extends Drive.Builder {
+
+      protected ImageConversionType format;
+      protected MediaType media;
+      protected long rawSize;
+
+      public Builder format(ImageConversionType format) {
+         this.format = format;
+         return this;
+      }
+
+      public Builder media(MediaType media) {
+         this.media = media;
+         return this;
+      }
+
+      public Builder rawSize(long rawSize) {
+         this.rawSize = rawSize;
+         return this;
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public Builder claimType(ClaimType claimType) {
+         return Builder.class.cast(super.claimType(claimType));
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public Builder readers(Iterable<String> readers) {
+         return Builder.class.cast(super.readers(readers));
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public Builder size(long size) {
+         return Builder.class.cast(super.size(size));
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public Builder uuid(String uuid) {
+         return Builder.class.cast(super.uuid(uuid));
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public Builder name(String name) {
+         return Builder.class.cast(super.name(name));
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public Builder tags(Iterable<String> tags) {
+         return Builder.class.cast(super.tags(tags));
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public Builder userMetadata(Map<String, String> userMetadata) {
+         return Builder.class.cast(super.userMetadata(userMetadata));
+      }
+
+      public static Builder fromDriveInfo(StandardDrive driveInfo) {
+         return new Builder().uuid(driveInfo.getUuid()).name(driveInfo.getName()).size(driveInfo.getSize())
+               .claimType(driveInfo.getClaimType()).readers(driveInfo.getReaders()).tags(driveInfo.getTags())
+               .userMetadata(driveInfo.getUserMetadata()).media(driveInfo.getMedia());
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public StandardDrive build() {
+         return new StandardDrive(uuid, name, size, claimType, readers, tags, userMetadata, format, media, rawSize);
+      }
+
+   }
+
+   protected final ImageConversionType format;
+   protected final MediaType media;
+   protected final long rawSize;
+
+   public StandardDrive(String uuid, String name, long size, ClaimType claimType, Iterable<String> readers,
+         Iterable<String> tags, Map<String, String> userMetadata, @Nullable ImageConversionType format,
+         MediaType media, long rawSize) {
+      super(uuid, name, size, claimType, readers, tags, userMetadata);
+      this.format = format;
+      this.media = checkNotNull(media, "media");
+      this.rawSize = rawSize;
+   }
+
+   public MediaType getMedia() {
+      return media;
+   }
+   
+   public ImageConversionType getFormat() {
+      return format;
+   }
+
+   public long getRawSize() {
+      return rawSize;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = super.hashCode();
+      result = prime * result + ((format == null) ? 0 : format.hashCode());
+      result = prime * result + ((media == null) ? 0 : media.hashCode());
+      result = prime * result + (int) (rawSize ^ (rawSize >>> 32));
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (!super.equals(obj))
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      StandardDrive other = (StandardDrive) obj;
+      if (format != other.format)
+         return false;
+      if (media == null) {
+         if (other.media != null)
+            return false;
+      } else if (!media.equals(other.media))
+         return false;
+      if (rawSize != other.rawSize)
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "StandardDrive [format=" + format + ", media=" + media + ", rawSize=" + rawSize + ", size=" + size
+            + ", claimType=" + claimType + ", readers=" + readers + ", uuid=" + uuid + ", name=" + name + ", tags="
+            + tags + ", userMetadata=" + userMetadata + "]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/WellKnownImage.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/WellKnownImage.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/WellKnownImage.java
index 9af9329..cbef29d 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/WellKnownImage.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/domain/WellKnownImage.java
@@ -16,7 +16,11 @@
  */
 package org.jclouds.elasticstack.domain;
 
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
 
@@ -25,20 +29,80 @@ import com.google.common.base.Objects;
  * @author Adrian Cole
  */
 public class WellKnownImage {
-   private String loginUser = "toor";
-   private String uuid;
-   private String description;
-   private OsFamily osFamily;
-   private String osVersion;
-   private int size;
-   private boolean is64bit = true;
 
-   // intended only for serialization
-   WellKnownImage() {
+   public static Builder builder() {
+      return new Builder();
+   }
 
+   public static class Builder {
+      private String loginUser;
+      private String uuid;
+      private String description;
+      private OsFamily osFamily;
+      private String osVersion;
+      private int size;
+      private boolean is64bit;
+
+      public Builder loginUser(String loginUser) {
+         this.loginUser = loginUser;
+         return this;
+      }
+
+      public Builder uuid(String uuid) {
+         this.uuid = uuid;
+         return this;
+      }
+
+      public Builder description(String description) {
+         this.description = description;
+         return this;
+      }
+
+      public Builder osFamily(OsFamily osFamily) {
+         this.osFamily = osFamily;
+         return this;
+      }
+
+      public Builder osVersion(String osVersion) {
+         this.osVersion = osVersion;
+         return this;
+      }
+
+      public Builder size(int size) {
+         this.size = size;
+         return this;
+      }
+
+      public Builder is64bit(boolean is64bit) {
+         this.is64bit = is64bit;
+         return this;
+      }
+
+      public WellKnownImage build() {
+         return new WellKnownImage(loginUser, uuid, description, osFamily, osVersion, size, is64bit);
+      }
    }
 
-   // performance isn't a concern on a infrequent object like this, so using shortcuts;
+   public static final String DEFAULT_USER = "toor";
+
+   private final String loginUser;
+   private final String uuid;
+   private final String description;
+   private final OsFamily osFamily;
+   private final String osVersion;
+   private final int size;
+   private final boolean is64bit;
+
+   public WellKnownImage(@Nullable String loginUser, String uuid, String description, OsFamily osFamily,
+         @Nullable String osVersion, int size, @Nullable Boolean is64bit) {
+      this.loginUser = firstNonNull(loginUser, DEFAULT_USER);
+      this.uuid = checkNotNull(uuid, "uuid cannot be null");
+      this.description = checkNotNull(description, "description cannot be null");
+      this.osFamily = checkNotNull(osFamily, "osFamily cannot be null");
+      this.osVersion = osVersion;
+      this.size = size;
+      this.is64bit = firstNonNull(is64bit, Boolean.TRUE);
+   }
 
    public String getUuid() {
       return uuid;
@@ -83,8 +147,8 @@ public class WellKnownImage {
    @Override
    public String toString() {
       return Objects.toStringHelper(this).add("uuid", uuid).add("description", description).add("osFamily", osFamily)
-               .add("osVersion", osVersion).add("size", size).add("is64bit", is64bit).add("loginUser", loginUser)
-               .toString();
+            .add("osVersion", osVersion).add("size", size).add("is64bit", is64bit).add("loginUser", loginUser)
+            .toString();
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet.java
new file mode 100644
index 0000000..19e3f336
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.nullToEmpty;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ReturnStringIf2xx;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Ignasi Barrera
+ */
+@Singleton
+public class ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet implements Function<HttpResponse, Set<StandardDrive>> {
+   private final ReturnStringIf2xx returnStringIf2xx;
+   private final ListOfKeyValuesDelimitedByBlankLinesToListOfMaps mapConverter;
+   private final MapToStandardDrive mapToStandardDrive;
+
+   @Inject
+   ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet(ReturnStringIf2xx returnStringIf2xx,
+         ListOfKeyValuesDelimitedByBlankLinesToListOfMaps mapConverter, MapToStandardDrive mapToStandardDrive) {
+      this.returnStringIf2xx = checkNotNull(returnStringIf2xx, "returnStringIf2xx");
+      this.mapConverter = checkNotNull(mapConverter, "mapConverter");
+      this.mapToStandardDrive = checkNotNull(mapToStandardDrive, "mapToStandardDrive");
+   }
+
+   @Override
+   public Set<StandardDrive> apply(HttpResponse response) {
+      String text = nullToEmpty(returnStringIf2xx.apply(response));
+      if (text.trim().equals("")) {
+         return ImmutableSet.<StandardDrive> of();
+      }
+      return ImmutableSet.copyOf(Iterables.transform(mapConverter.apply(text), mapToStandardDrive));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/MapToStandardDrive.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/MapToStandardDrive.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/MapToStandardDrive.java
new file mode 100644
index 0000000..e5a7ea2
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/MapToStandardDrive.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.functions;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+
+import org.jclouds.elasticstack.domain.ClaimType;
+import org.jclouds.elasticstack.domain.ImageConversionType;
+import org.jclouds.elasticstack.domain.MediaType;
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Maps;
+
+/**
+ * @author Ignasi Barrera
+ */
+@Singleton
+public class MapToStandardDrive implements Function<Map<String, String>, StandardDrive> {
+
+   @Resource
+   protected Logger logger = Logger.NULL;
+
+   @Override
+   public StandardDrive apply(Map<String, String> from) {
+      if (from.isEmpty())
+         return null;
+      StandardDrive.Builder builder = new StandardDrive.Builder();
+      builder.name(from.get("name"));
+      builder.media(MediaType.fromValue(from.get("media")));
+      if (from.containsKey("tags"))
+         builder.tags(Splitter.on(' ').split(from.get("tags")));
+      builder.uuid(from.get("drive"));
+      if (from.containsKey("claim:type"))
+         builder.claimType(ClaimType.fromValue(from.get("claim:type")));
+      if (from.containsKey("readers"))
+         builder.readers(Splitter.on(' ').split(from.get("readers")));
+      if (from.containsKey("size"))
+         builder.size(Long.valueOf(from.get("size")));
+      if (from.containsKey("rawsize"))
+         builder.rawSize(Long.valueOf(from.get("rawsize")));
+      if (from.containsKey("format"))
+         builder.format(ImageConversionType.fromValue(from.get("format")));
+      Map<String, String> metadata = Maps.newLinkedHashMap();
+      for (Entry<String, String> entry : from.entrySet()) {
+         String key = entry.getKey();
+         if (key.startsWith("user:"))
+            metadata.put(key.substring(key.indexOf(':') + 1), entry.getValue());
+      }
+      builder.userMetadata(metadata);
+      try {
+         return builder.build();
+      } catch (NullPointerException e) {
+         logger.trace("entry missing data: %s; %s", e.getMessage(), from);
+         return null;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/SplitNewlines.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/SplitNewlines.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/SplitNewlines.java
index 48858fb..0b53d92 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/SplitNewlines.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/functions/SplitNewlines.java
@@ -16,8 +16,6 @@
  */
 package org.jclouds.elasticstack.functions;
 
-import static com.google.common.collect.Sets.newTreeSet;
-
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -28,6 +26,7 @@ import org.jclouds.http.functions.ReturnStringIf2xx;
 
 import com.google.common.base.Function;
 import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * 
@@ -44,6 +43,8 @@ public class SplitNewlines implements Function<HttpResponse, Set<String>> {
 
    @Override
    public Set<String> apply(HttpResponse response) {
-      return newTreeSet(Splitter.on('\n').omitEmptyStrings().split(returnStringIf200.apply(response)));
+      String payload = returnStringIf200.apply(response);
+      return payload == null ? ImmutableSet.<String> of() : ImmutableSet.copyOf(Splitter.on('\n').omitEmptyStrings()
+            .split(payload));
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/handlers/ElasticStackErrorHandler.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/handlers/ElasticStackErrorHandler.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/handlers/ElasticStackErrorHandler.java
index 5b6e1a4..af85ad3 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/handlers/ElasticStackErrorHandler.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/handlers/ElasticStackErrorHandler.java
@@ -16,6 +16,8 @@
  */
 package org.jclouds.elasticstack.handlers;
 
+import static org.jclouds.http.HttpUtils.releasePayload;
+
 import java.io.IOException;
 
 import javax.annotation.Resource;
@@ -28,10 +30,9 @@ import org.jclouds.http.HttpResponseException;
 import org.jclouds.logging.Logger;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.ResourceNotFoundException;
-import org.jclouds.util.Closeables2;
 import org.jclouds.util.Strings2;
 
-import com.google.common.base.Throwables;
+import com.google.common.io.Closeables;
 
 /**
  * This will parse and set an appropriate exception on the command object.
@@ -80,7 +81,11 @@ public class ElasticStackErrorHandler implements HttpErrorHandler {
             break;
          }
       } finally {
-         Closeables2.closeQuietly(response.getPayload());
+         try {
+            Closeables.close(response.getPayload(), true);
+         } catch (IOException e) {
+            // Unreachable code
+         }
          command.setException(exception);
       }
    }
@@ -93,11 +98,7 @@ public class ElasticStackErrorHandler implements HttpErrorHandler {
       } catch (IOException e) {
          throw new RuntimeException(e);
       } finally {
-         try {
-            response.getPayload().getInput().close();
-         } catch (IOException e) {
-            Throwables.propagate(e);
-         }
+         releasePayload(response);
       }
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/suppliers/StandardDiskImageSupplier.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/suppliers/StandardDiskImageSupplier.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/suppliers/StandardDiskImageSupplier.java
new file mode 100644
index 0000000..9e5fb91
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/suppliers/StandardDiskImageSupplier.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.suppliers;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.elasticstack.ElasticStackApi;
+import org.jclouds.elasticstack.domain.MediaType;
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.elasticstack.domain.WellKnownImage;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Supplies the pre-installed images.
+ * 
+ * @author Ignasi Barrera
+ * 
+ */
+@Singleton
+public class StandardDiskImageSupplier implements WellKnownImageSupplier {
+
+   private final ElasticStackApi api;
+
+   private final Function<StandardDrive, WellKnownImage> standardDriveToWellKnownImage;
+
+   @Inject
+   StandardDiskImageSupplier(ElasticStackApi api, Function<StandardDrive, WellKnownImage> standardDriveToWellKnownImage) {
+      this.api = checkNotNull(api, "api");
+      this.standardDriveToWellKnownImage = checkNotNull(standardDriveToWellKnownImage, "standardDriveToWellKnownImage");
+   }
+
+   @Override
+   public List<WellKnownImage> get() {
+      ImmutableList.Builder<WellKnownImage> images = ImmutableList.builder();
+      for (StandardDrive drive : api.listStandardDriveInfo()) {
+         if (drive.getMedia() == MediaType.DISK) {
+            images.add(standardDriveToWellKnownImage.apply(drive));
+         }
+      }
+      return images.build();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/java/org/jclouds/elasticstack/suppliers/WellKnownImageSupplier.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/suppliers/WellKnownImageSupplier.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/suppliers/WellKnownImageSupplier.java
new file mode 100644
index 0000000..8d95612
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/suppliers/WellKnownImageSupplier.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.suppliers;
+
+import java.util.List;
+
+import org.jclouds.elasticstack.domain.WellKnownImage;
+
+import com.google.common.base.Supplier;
+import com.google.inject.ImplementedBy;
+
+/**
+ * Supplies the well known images
+ * 
+ * @author Ignasi Barrera
+ * 
+ */
+@ImplementedBy(StandardDiskImageSupplier.class)
+public interface WellKnownImageSupplier extends Supplier<List<WellKnownImage>> {
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/resources/elasticstack/preinstalled_images.json
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/resources/elasticstack/preinstalled_images.json b/apis/elasticstack/src/main/resources/elasticstack/preinstalled_images.json
deleted file mode 100644
index dadd7b7..0000000
--- a/apis/elasticstack/src/main/resources/elasticstack/preinstalled_images.json
+++ /dev/null
@@ -1,72 +0,0 @@
-[
-  {
-      "uuid": "38df0986-4d85-4b76-b502-3878ffc80161",
-      "description": "CentOS Linux 5.5",
-      "osFamily": "CENTOS",
-      "osVersion": "5.5",
-      "size": "3"
-  },
-  {
-      "uuid": "980cf63c-f21e-4382-997b-6541d5809629",
-      "description": "Debian Linux 5.0",
-      "osFamily": "DEBIAN",
-      "osVersion": "5.0",
-      "size": "1"
-  },
-  {
-      "uuid": "aee5589a-88c3-43ef-bb0a-9cab6e64192d",
-      "description": "Ubuntu Linux 10.04",
-      "osFamily": "UBUNTU",
-      "osVersion": "10.04",
-      "size": "1"
-  },
-  {
-      "uuid": "bf1d943e-2a55-46bb-a8c7-6099e44a3dde",
-      "description": "Ubuntu Linux 8.10: Base system with X",
-      "osFamily": "UBUNTU",
-      "osVersion": "8.10",
-      "size": "3"
-  },
-  {
-      "uuid": "757586d5-f1e9-4d9c-b215-5a391c9a24bf",
-      "description": "Ubuntu Linux 9.04: Base system with X",
-      "osFamily": "UBUNTU",
-      "osVersion": "9.04",
-      "size": "3"
-  },
-  {
-      "uuid": "b9d0eb72-d273-43f1-98e3-0d4b87d372c0",
-      "description": "Windows Web Server 2008",
-      "osFamily": "WINDOWS",
-      "osVersion": "2008",
-      "size": "13"
-  },
-  {
-      "uuid": "b405b598-4ae4-4ba8-8a2b-a9487d693f34",
-      "description": "Windows Web Server 2008 R2",
-      "osFamily": "WINDOWS",
-      "osVersion": "2008 R2",
-      "size": "13"
-  },
-  {
-      "uuid": "9397d327-3bf6-46a2-abf6-69553dbb6927",
-      "description": "Windows Web Server 2008 R2 + SQL Server",
-      "osFamily": "WINDOWS",
-      "osVersion": "2008 R2",
-      "size": "13"
-  },
-  {
-      "uuid": "10a88d1c-6575-46e3-8d2c-7744065ea530",
-      "description": "Windows Server 2008 Standard R2",
-      "osFamily": "WINDOWS",
-      "osVersion": "2008 R2",
-      "size": "13"
-  },
-  {
-      "uuid": "662c5b3f-9828-4aa2-a866-7cfa53798cdf",
-      "description": "Windows Server 2008 Standard R2 + SQL Server",
-      "osFamily": "WINDOWS",
-      "osVersion": "2008 R2",
-      "size": "13"
-  }
-]

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/main/resources/preinstalled_images.readme
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/resources/preinstalled_images.readme b/apis/elasticstack/src/main/resources/preinstalled_images.readme
deleted file mode 100644
index 5f6bb69..0000000
--- a/apis/elasticstack/src/main/resources/preinstalled_images.readme
+++ /dev/null
@@ -1,7 +0,0 @@
-the images collection listed in the computeservice api is populated from preinstalled_images.json in a resource path corresponding to the provider name.  There's no way to list standard images via api in cloudstack installs.  This is the process:
-  1. log into the portal
-  2. View Source
-  3. look for the image you want ex 'Debian Linux 6.0.1'
-  4. find the UUID, which will look like:  6aa953cc-3395-4e8d-938e-65c74fd20334
-
-Other details can be found by contacting the provider and asking them

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java
index f18f2f9..f482d89 100644
--- a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java
@@ -18,6 +18,7 @@ package org.jclouds.elasticstack;
 import static java.util.concurrent.TimeUnit.SECONDS;
 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 java.io.IOException;
@@ -38,6 +39,7 @@ import org.jclouds.elasticstack.domain.Model;
 import org.jclouds.elasticstack.domain.Server;
 import org.jclouds.elasticstack.domain.ServerInfo;
 import org.jclouds.elasticstack.domain.ServerStatus;
+import org.jclouds.elasticstack.domain.StandardDrive;
 import org.jclouds.elasticstack.predicates.DriveClaimed;
 import org.jclouds.elasticstack.util.Servers;
 import org.jclouds.io.Payloads;
@@ -114,12 +116,26 @@ public class ElasticStackApiLiveTest extends BaseComputeServiceContextLiveTest {
       Set<String> drives = client.listDrives();
       assertNotNull(drives);
    }
+   
+   @Test
+   public void testListStandardDrives() throws Exception {
+      Set<String> drives = client.listStandardDrives();
+      assertNotNull(drives);
+      assertFalse(drives.isEmpty(), "standard drive list should not be empty");
+   }
 
    @Test
    public void testListDriveInfo() throws Exception {
       Set<? extends DriveInfo> drives = client.listDriveInfo();
       assertNotNull(drives);
    }
+   
+   @Test
+   public void testListStandardDriveInfo() throws Exception {
+      Set<? extends StandardDrive> drives = client.listStandardDriveInfo();
+      assertNotNull(drives);
+      assertFalse(drives.isEmpty(), "standard drive list should not be empty");
+   }
 
    @Test
    public void testGetDrive() throws Exception {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java
index aa62b83..9618826 100644
--- a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java
@@ -38,6 +38,8 @@ import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesTo
 import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet;
 import org.jclouds.elasticstack.functions.ReturnPayload;
 import org.jclouds.elasticstack.functions.SplitNewlines;
+import org.jclouds.elasticstack.suppliers.MockStandardDiskImageSupplier;
+import org.jclouds.elasticstack.suppliers.WellKnownImageSupplier;
 import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.filters.BasicAuthentication;
@@ -51,6 +53,9 @@ import org.testng.annotations.Test;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.reflect.Invokable;
+import com.google.inject.AbstractModule;
+import com.google.inject.Module;
+import com.google.inject.Scopes;
 /**
  * Tests behavior of {@code ElasticStackApi}
  * 
@@ -448,4 +453,14 @@ public class ElasticStackApiTest extends BaseAsyncClientTest<ElasticStackApi> {
       return new ElasticStackApiMetadata();
    }
 
+   @Override
+   protected Module createModule() {
+      return new AbstractModule() {
+         @Override
+         protected void configure() {
+            bind(WellKnownImageSupplier.class).to(MockStandardDiskImageSupplier.class).in(Scopes.SINGLETON);
+         }
+      };
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackMockTest.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackMockTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackMockTest.java
new file mode 100644
index 0000000..e6abbb4
--- /dev/null
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackMockTest.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack;
+
+import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
+import static org.jclouds.Constants.PROPERTY_IDENTITY;
+import static org.jclouds.util.Strings2.toStringAndClose;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.ws.rs.core.HttpHeaders;
+
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.http.BaseMockWebServerTest;
+import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.inject.Module;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+/**
+ * Mock tests for the {@link ElasticStackApi} class.
+ * 
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "ElasticStackMockTest")
+public class ElasticStackMockTest extends BaseMockWebServerTest {
+
+   public void testListStandardDrives() throws IOException, InterruptedException {
+      MockWebServer server = mockWebServer(new MockResponse()
+            .setBody(payloadFromResource("/standard_drives_uuids.txt")));
+      ElasticStackApi api = api(ElasticStackApi.class, server.getUrl("/").toString());
+
+      try {
+         Set<String> standardDrives = api.listStandardDrives();
+         assertEquals(standardDrives.size(), 36);
+
+         RecordedRequest request = server.takeRequest();
+         assertAuthentication(request);
+         assertEquals(request.getRequestLine(),
+               String.format("GET /drives/list/standard HTTP/1.1", server.getUrl("/").toString()));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testListStandardDriveInfo() throws IOException, InterruptedException {
+      MockWebServer server = mockWebServer(new MockResponse().setBody(payloadFromResource("/standard_drives.txt")));
+      ElasticStackApi api = api(ElasticStackApi.class, server.getUrl("/").toString());
+
+      try {
+         Set<StandardDrive> standardDrives = api.listStandardDriveInfo();
+         assertEquals(standardDrives.size(), 36);
+
+         RecordedRequest request = server.takeRequest();
+         assertAuthentication(request);
+         assertEquals(request.getRequestLine(),
+               String.format("GET /drives/info/standard HTTP/1.1", server.getUrl("/").toString()));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   private static void assertAuthentication(final RecordedRequest request) throws InterruptedException {
+      assertEquals(request.getHeader(HttpHeaders.AUTHORIZATION), "Basic dXVpZDphcGlrZXk=");
+   }
+
+   private byte[] payloadFromResource(String resource) {
+      try {
+         return toStringAndClose(getClass().getResourceAsStream(resource)).getBytes(Charsets.UTF_8);
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   @Override
+   protected void addOverrideProperties(Properties props) {
+      props.setProperty(PROPERTY_IDENTITY, "uuid");
+      props.setProperty(PROPERTY_CREDENTIAL, "apikey");
+   }
+
+   @Override
+   protected Module createConnectionModule() {
+      return new JavaUrlHttpCommandExecutorServiceModule();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/test/java/org/jclouds/elasticstack/compute/functions/StandardDriveToWellKnownImageTest.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/compute/functions/StandardDriveToWellKnownImageTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/compute/functions/StandardDriveToWellKnownImageTest.java
new file mode 100644
index 0000000..9593aee
--- /dev/null
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/compute/functions/StandardDriveToWellKnownImageTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.UUID;
+
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.elasticstack.domain.MediaType;
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.elasticstack.domain.WellKnownImage;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link StandardDriveToWellKnownImage} class.
+ * 
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "StandardDriveToWellKnownImageTest")
+public class StandardDriveToWellKnownImageTest {
+
+   private StandardDriveToWellKnownImage function = new StandardDriveToWellKnownImage();
+   
+   public void testUnknownOperatingSystemParsing() {
+      assertOS("Foo Linux 6.5", OsFamily.UNRECOGNIZED, "6.5", true);
+   }
+   
+   public void testOperatingSystemWithoutVersionParsing() {
+      assertOS("Ubuntu Linux", OsFamily.UBUNTU, null, true);
+   }
+
+   public void testKnownOperatingSystemParsing() {
+
+      // Elastichosts
+      assertOS("centOS Linux 6.5", OsFamily.CENTOS, "6.5", true);
+      assertOS("Debian Linux 7.4 (Wheezy)", OsFamily.DEBIAN, "7.4", true);
+      assertOS("Ubuntu Linux 12.04.1 LTS (Precise Pangolin)", OsFamily.UBUNTU, "12.04.1", true);
+      assertOS("Ubuntu Linux 13.10 (Saucy Salamander)", OsFamily.UBUNTU, "13.10", true);
+      assertOS("Ubuntu 14.04 LTS (Trusty Tahr)", OsFamily.UBUNTU, "14.04", true);
+      assertOS("Windows Server 2012", OsFamily.WINDOWS, "2012", true);
+      assertOS("Windows Server 2012 + SQL", OsFamily.WINDOWS, "2012 + SQL", true);
+      assertOS("Windows Server 2012 R2", OsFamily.WINDOWS, "2012 R2", true);
+      assertOS("Windows Standard 2008 R2", OsFamily.WINDOWS, "2008 R2", true);
+      assertOS("Windows Standard 2008 R2 + SQL", OsFamily.WINDOWS, "2008 R2 + SQL", true);
+      assertOS("Windows Web Server 2008 R2", OsFamily.WINDOWS, "2008 R2", true);
+      assertOS("Windows Web Server 2008 R2 + SQL", OsFamily.WINDOWS, "2008 R2 + SQL", true);
+
+      // Go2Cloud
+      assertOS("Ubuntu 10.10", OsFamily.UBUNTU, "10.10", true);
+      assertOS("Debian 6.0.2.1", OsFamily.DEBIAN, "6.0.2.1", true);
+      assertOS("Windows 2008 R2 (x64) with SP1", OsFamily.WINDOWS, "2008 R2 (x64) with SP1", true);
+      assertOS("Windows 8 Developer Preview (x64)", OsFamily.WINDOWS, "8 Developer Preview (x64)", true);
+
+      // OpenHosting
+      assertOS("CentOS Linux 5.5 64", OsFamily.CENTOS, "5.5", true);
+      assertOS("CentOS Linux 5.6 64", OsFamily.CENTOS, "5.6", true);
+      assertOS("CentOS Linux 5.7 64", OsFamily.CENTOS, "5.7", true);
+      assertOS("Debian Linux 5.0", OsFamily.DEBIAN, "5.0", true);
+      assertOS("Debian Linux 6 (Squeeze) 64", OsFamily.DEBIAN, "6", true);
+      assertOS("Ubuntu 10.04.3 LTS (lucid) Server 64", OsFamily.UBUNTU, "10.04.3", true);
+      assertOS("Windows 2008 R2 Standard Edition", OsFamily.WINDOWS, "2008 R2 Standard Edition", true);
+
+      // Skalicloud
+      assertOS("CentOS 5.5 -32bit", OsFamily.CENTOS, "5.5", false);
+      assertOS("CentOS 5.5 -64bit", OsFamily.CENTOS, "5.5", true);
+      assertOS("CentOS 5.6 -32bit", OsFamily.CENTOS, "5.6", false);
+      assertOS("CentOS 5.6 -64bit", OsFamily.CENTOS, "5.6", true);
+      assertOS("Debian 5 -32bit", OsFamily.DEBIAN, "5", false);
+      assertOS("Debian 5 -64bit", OsFamily.DEBIAN, "5", true);
+      assertOS("Debian 6 -64bit -Experimental", OsFamily.DEBIAN, "6", true);
+      assertOS("Ubuntu Server 10.04 -32bit", OsFamily.UBUNTU, "10.04", false);
+      assertOS("Ubuntu Server 10.04 -64bit", OsFamily.UBUNTU, "10.04", true);
+      assertOS("Ubuntu Server 10.10 -32bit", OsFamily.UBUNTU, "10.10", false);
+      assertOS("Ubuntu Server 10.10 -64bit", OsFamily.UBUNTU, "10.10", true);
+      assertOS("Windows 2008R2 Web Edition", OsFamily.WINDOWS, "2008R2 Web Edition", true);
+      assertOS("Windows Server 2008R2 Standard", OsFamily.WINDOWS, "2008R2 Standard", true);
+
+      // ServerLove
+      assertOS("CentOS Linux 5.7", OsFamily.CENTOS, "5.7", true);
+      assertOS("CentOS Linux 6.2", OsFamily.CENTOS, "6.2", true);
+      assertOS("Debian Linux 6.0", OsFamily.DEBIAN, "6.0", true);
+      assertOS("Ubuntu 10.04 LTS", OsFamily.UBUNTU, "10.04", true);
+      assertOS("Ubuntu 12.04 LTS", OsFamily.UBUNTU, "12.04", true);
+      assertOS("Windows Server 2008 R2 Standard", OsFamily.WINDOWS, "2008 R2 Standard", true);
+      assertOS("Windows Server 2008 R2 Standard SP1 with SQL Server 2008 R2 Web Edition", OsFamily.WINDOWS,
+            "2008 R2 Standard SP1 with SQL Server 2008 R2 Web Edition", true);
+      assertOS("Windows Server 2012 Standard", OsFamily.WINDOWS, "2012 Standard", true);
+      assertOS("Windows Web Server 2008 R2", OsFamily.WINDOWS, "2008 R2", true);
+      assertOS("Windows Web Server 2008 R2 SP1 with SQL Server 2008 R2 Web Edition", OsFamily.WINDOWS,
+            "2008 R2 SP1 with SQL Server 2008 R2 Web Edition", true);
+   }
+
+   private void assertOS(String name, OsFamily expectedFamily, String expectedVersion, boolean expectedIs64bit) {
+      StandardDrive drive = standardDrive(name);
+      WellKnownImage image = function.apply(drive);
+
+      assertEquals(image.getOsFamily(), expectedFamily, String.format("Parsing family for [%s]:", name));
+      assertEquals(image.getOsVersion(), expectedVersion, String.format("Parsing version for [%s]:", name));
+      assertEquals(image.is64bit(), expectedIs64bit, String.format("Parsing arch for [%s]:", name));
+   }
+
+   private static StandardDrive standardDrive(String name) {
+      return new StandardDrive.Builder().uuid(UUID.randomUUID().toString()).size(1).name(name).media(MediaType.DISK)
+            .build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/test/java/org/jclouds/elasticstack/functions/ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSetTest.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/functions/ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSetTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/functions/ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSetTest.java
new file mode 100644
index 0000000..35e1fab
--- /dev/null
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/functions/ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSetTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+
+/**
+ * @author Ignasi Barrera
+ */
+@Test(groups = { "unit" })
+public class ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSetTest {
+
+   private static final ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet FN = Guice.createInjector().getInstance(
+         ListOfKeyValuesDelimitedByBlankLinesToStandardDriveSet.class);
+
+   public void testNone() {
+      assertEquals(FN.apply(HttpResponse.builder().statusCode(200).message("").payload("").build()), ImmutableSet.<StandardDrive> of());
+      assertEquals(FN.apply(HttpResponse.builder().statusCode(200).message("").payload("\n\n").build()), ImmutableSet.<StandardDrive> of());
+      assertEquals(FN.apply(HttpResponse.builder().statusCode(200).message("").build()), ImmutableSet.<StandardDrive> of());
+   }
+
+   public void testOne() {
+      assertEquals(FN.apply(HttpResponse.builder().statusCode(200).message("").payload(MapToStandardDriveTest.class
+            .getResourceAsStream("/standard_drive.txt")).build()), ImmutableSet.<StandardDrive> of(MapToStandardDriveTest.ONE));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/test/java/org/jclouds/elasticstack/functions/MapToStandardDriveTest.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/functions/MapToStandardDriveTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/functions/MapToStandardDriveTest.java
new file mode 100644
index 0000000..0205ad5
--- /dev/null
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/functions/MapToStandardDriveTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.jclouds.elasticstack.domain.ClaimType;
+import org.jclouds.elasticstack.domain.ImageConversionType;
+import org.jclouds.elasticstack.domain.MediaType;
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Ignasi Barrera
+ */
+@Test(groups = { "unit" })
+public class MapToStandardDriveTest {
+   public static StandardDrive ONE = new StandardDrive.Builder()
+         .name("Windows Web Server 2008 R2")
+         .uuid("11b84345-7169-4279-8038-18d6ba1a7712")//
+         .claimType(ClaimType.SHARED)
+         .readers(ImmutableSet.of("ffffffff-ffff-ffff-ffff-ffffffffffff"))//
+         .size(4743757824L)//
+         .rawSize(11811160064L)//
+         .format(ImageConversionType.GZIP)//
+         .media(MediaType.DISK)//
+         .build();
+
+   private static final MapToStandardDrive MAP_TO_STANDARD_DRIVE = new MapToStandardDrive();
+
+   public void testEmptyMapReturnsNull() {
+      assertEquals(MAP_TO_STANDARD_DRIVE.apply(ImmutableMap.<String, String> of()), null);
+   }
+
+   public void testBasics() {
+      StandardDrive expects = new StandardDrive.Builder().name("foo").size(100l).media(MediaType.DISK)
+            .format(ImageConversionType.GZIP).rawSize(5l).build();
+      assertEquals(MAP_TO_STANDARD_DRIVE.apply(ImmutableMap.of("name", "foo", "size", "100", "format", "gzip", "media",
+            "disk", "rawsize", "5")), expects);
+   }
+
+   public void testComplete() throws IOException {
+      Map<String, String> input = new ListOfKeyValuesDelimitedByBlankLinesToListOfMaps().apply(
+            Strings2.toStringAndClose(MapToStandardDriveTest.class.getResourceAsStream("/standard_drive.txt"))).get(0);
+      assertEquals(MAP_TO_STANDARD_DRIVE.apply(input), ONE);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/test/java/org/jclouds/elasticstack/suppliers/MockStandardDiskImageSupplier.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/suppliers/MockStandardDiskImageSupplier.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/suppliers/MockStandardDiskImageSupplier.java
new file mode 100644
index 0000000..cf91cfb
--- /dev/null
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/suppliers/MockStandardDiskImageSupplier.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.elasticstack.suppliers;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.transform;
+import static org.jclouds.util.Strings2.toStringAndClose;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.elasticstack.domain.StandardDrive;
+import org.jclouds.elasticstack.domain.WellKnownImage;
+import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToListOfMaps;
+import org.jclouds.elasticstack.functions.MapToStandardDrive;
+
+import com.google.common.base.Function;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Mock {@link WellKnownImageSupplier} to be used in tests.
+ * 
+ * @author Ignasi Barrera
+ */
+@Singleton
+public class MockStandardDiskImageSupplier implements WellKnownImageSupplier {
+
+   private final Function<StandardDrive, WellKnownImage> standardDriveToWellKnownImage;
+   private final ListOfKeyValuesDelimitedByBlankLinesToListOfMaps mapConverter;
+   private final MapToStandardDrive mapToStandardDrive;
+   
+   @Inject
+   public MockStandardDiskImageSupplier(Function<StandardDrive, WellKnownImage> standardDriveToWellKnownImage,
+         ListOfKeyValuesDelimitedByBlankLinesToListOfMaps mapConverter, MapToStandardDrive mapToStandardDrive) {
+      this.standardDriveToWellKnownImage = checkNotNull(standardDriveToWellKnownImage, "standardDriveToWellKnownImage cannot be null");
+      this.mapConverter = checkNotNull(mapConverter, "mapConverter cannot be null");
+      this.mapToStandardDrive = checkNotNull(mapToStandardDrive, "mapToStandardDrive cannot be null");
+   }
+
+   @Override
+   public List<WellKnownImage> get() {
+      try {
+         String mockDrives = toStringAndClose(getClass().getResourceAsStream("/standard_drives.txt"));
+         Iterable<StandardDrive> parsedDrives = transform(mapConverter.apply(mockDrives), mapToStandardDrive);
+         return ImmutableList.copyOf(transform(parsedDrives, standardDriveToWellKnownImage));
+      } catch (IOException ex) {
+         throw Throwables.propagate(ex);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/fbf30a73/apis/elasticstack/src/test/resources/standard_drive.txt
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/resources/standard_drive.txt b/apis/elasticstack/src/test/resources/standard_drive.txt
new file mode 100644
index 0000000..01fb5cd
--- /dev/null
+++ b/apis/elasticstack/src/test/resources/standard_drive.txt
@@ -0,0 +1,8 @@
+claim:type shared
+drive 11b84345-7169-4279-8038-18d6ba1a7712
+format gzip
+media disk
+name Windows Web Server 2008 R2
+rawsize 11811160064
+readers ffffffff-ffff-ffff-ffff-ffffffffffff
+size 4743757824
\ No newline at end of file