Issue 798: merge for potential 1.3.0-rc-2
diff --git a/apis/atmos/pom.xml b/apis/atmos/pom.xml
index 7b18e5e..eab04c3 100644
--- a/apis/atmos/pom.xml
+++ b/apis/atmos/pom.xml
@@ -36,7 +36,8 @@
   <properties>
     <test.initializer>org.jclouds.atmos.blobstore.integration.AtmosStorageTestInitializer</test.initializer>
     <test.atmos.endpoint>https://accesspoint.atmos.com</test.atmos.endpoint>
-    <test.atmos.apiversion>1.3.0</test.atmos.apiversion>
+    <test.atmos.api-version>1.3.0</test.atmos.api-version>
+    <test.atmos.build-version></test.atmos.build-version>
     <test.atmos.identity>FIXME</test.atmos.identity>
     <test.atmos.credential>FIXME</test.atmos.credential>
   </properties>
@@ -95,7 +96,8 @@
                     <jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
                     <jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
                     <test.atmos.endpoint>${test.atmos.endpoint}</test.atmos.endpoint>
-                    <test.atmos.apiversion>${test.atmos.apiversion}</test.atmos.apiversion>
+                    <test.atmos.api-version>${test.atmos.api-version}</test.atmos.api-version>
+                    <test.atmos.build-version>${test.atmos.build-version}</test.atmos.build-version>
                     <test.atmos.identity>${test.atmos.identity}</test.atmos.identity>
                     <test.atmos.credential>${test.atmos.credential}</test.atmos.credential>
                   </systemPropertyVariables>
diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosTestInitializer.java b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosTestInitializer.java
index f77aec3..1cc02f1 100644
--- a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosTestInitializer.java
+++ b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosTestInitializer.java
@@ -38,10 +38,10 @@
    }
 
    @Override
-   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiversion,
-            String app, String identity, String credential) throws IOException {
+   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiVersion,
+            String buildVersion, String app, String identity, String credential) throws IOException {
       return new BlobStoreContextFactory().createContext(provider, ImmutableSet.of(configurationModule,
-               new Log4JLoggingModule()), setupProperties(endpoint, apiversion, identity, credential));
+               new Log4JLoggingModule()), setupProperties(endpoint, apiVersion, buildVersion, identity, credential));
    }
 
 }
diff --git a/apis/byon/src/main/java/org/jclouds/byon/functions/NodesFromYamlStream.java b/apis/byon/src/main/java/org/jclouds/byon/functions/NodesFromYamlStream.java
index 9505b7f..4ad13e3 100644
--- a/apis/byon/src/main/java/org/jclouds/byon/functions/NodesFromYamlStream.java
+++ b/apis/byon/src/main/java/org/jclouds/byon/functions/NodesFromYamlStream.java
@@ -35,9 +35,9 @@
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
-import com.google.common.cache.LoadingCache;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 
diff --git a/apis/cloudfiles/pom.xml b/apis/cloudfiles/pom.xml
index cc1fac2..0ab0549 100644
--- a/apis/cloudfiles/pom.xml
+++ b/apis/cloudfiles/pom.xml
@@ -36,7 +36,8 @@
   <properties>
     <test.initializer>org.jclouds.cloudfiles.blobstore.integration.CloudFilesTestInitializer</test.initializer>
     <test.cloudfiles.endpoint>https://auth.api.rackspacecloud.com</test.cloudfiles.endpoint>
-    <test.cloudfiles.apiversion>1.0</test.cloudfiles.apiversion>
+    <test.cloudfiles.api-version>1.0</test.cloudfiles.api-version>
+    <test.cloudfiles.build-version></test.cloudfiles.build-version>
     <test.cloudfiles.identity>${test.rackspace.identity}</test.cloudfiles.identity>
     <test.cloudfiles.credential>${test.rackspace.credential}</test.cloudfiles.credential>
   </properties>
@@ -110,7 +111,8 @@
                     <jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
                     <jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
                     <test.cloudfiles.endpoint>${test.cloudfiles.endpoint}</test.cloudfiles.endpoint>
-                    <test.cloudfiles.apiversion>${test.cloudfiles.apiversion}</test.cloudfiles.apiversion>
+                    <test.cloudfiles.api-version>${test.cloudfiles.api-version}</test.cloudfiles.api-version>
+                    <test.cloudfiles.build-version>${test.cloudfiles.build-version}</test.cloudfiles.build-version>
                     <test.cloudfiles.identity>${test.cloudfiles.identity}</test.cloudfiles.identity>
                     <test.cloudfiles.credential>${test.cloudfiles.credential}</test.cloudfiles.credential>
                   </systemPropertyVariables>
diff --git a/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/blobstore/functions/PublicUriForObjectInfo.java b/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/blobstore/functions/PublicUriForObjectInfo.java
index aa462f0..e78b301 100644
--- a/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/blobstore/functions/PublicUriForObjectInfo.java
+++ b/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/blobstore/functions/PublicUriForObjectInfo.java
@@ -52,6 +52,9 @@
       try {
          return uriBuilders.get().uri(cdnContainer.getUnchecked(from.getContainer())).path(from.getName()).replaceQuery("")
                   .build();
+      } catch (NullPointerException e) {
+         // nulls not permitted from cache loader
+         return null;
       } catch (CacheLoader.InvalidCacheLoadException e) {
          // nulls not permitted from cache loader
          return null;
diff --git a/apis/cloudloadbalancers/pom.xml b/apis/cloudloadbalancers/pom.xml
index 744e6ac..f414c69 100644
--- a/apis/cloudloadbalancers/pom.xml
+++ b/apis/cloudloadbalancers/pom.xml
@@ -35,7 +35,8 @@
   
    <properties>
     <test.cloudloadbalancers.endpoint>https://auth.api.rackspacecloud.com</test.cloudloadbalancers.endpoint>
-    <test.cloudloadbalancers.apiversion>1.0</test.cloudloadbalancers.apiversion>
+    <test.cloudloadbalancers.api-version>1.0</test.cloudloadbalancers.api-version>
+    <test.cloudloadbalancers.build-version></test.cloudloadbalancers.build-version>
     <test.cloudloadbalancers.identity>${test.rackspace.identity}</test.cloudloadbalancers.identity>
     <test.cloudloadbalancers.credential>${test.rackspace.credential}</test.cloudloadbalancers.credential>
   </properties>
@@ -96,7 +97,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.cloudloadbalancers.endpoint>${test.cloudloadbalancers.endpoint}</test.cloudloadbalancers.endpoint>
-                    <test.cloudloadbalancers.apiversion>${test.cloudloadbalancers.apiversion}</test.cloudloadbalancers.apiversion>
+                    <test.cloudloadbalancers.api-version>${test.cloudloadbalancers.api-version}</test.cloudloadbalancers.api-version>
+                    <test.cloudloadbalancers.build-version>${test.cloudloadbalancers.build-version}</test.cloudloadbalancers.build-version>
                     <test.cloudloadbalancers.identity>${test.cloudloadbalancers.identity}</test.cloudloadbalancers.identity>
                     <test.cloudloadbalancers.credential>${test.cloudloadbalancers.credential}</test.cloudloadbalancers.credential>
                   </systemPropertyVariables>
diff --git a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
index 3bd986f..b88c1f3 100644
--- a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
+++ b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
@@ -38,6 +38,7 @@
 import org.jclouds.cloudloadbalancers.handlers.ParseCloudLoadBalancersErrorFromHttpResponse;
 import org.jclouds.cloudloadbalancers.reference.RackspaceConstants;
 import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpRetryHandler;
 import org.jclouds.http.RequiresHttp;
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
@@ -51,6 +52,7 @@
 import org.jclouds.logging.Logger.LoggerFactory;
 import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
 import org.jclouds.openstack.config.OpenStackAuthenticationModule;
+import org.jclouds.openstack.handlers.RetryOnRenew;
 import org.jclouds.openstack.reference.AuthHeaders;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.config.RestClientModule;
@@ -154,4 +156,8 @@
             ParseCloudLoadBalancersErrorFromHttpResponse.class);
    }
 
+   @Override
+   protected void bindRetryHandlers() {
+      bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class);
+   }
 }
diff --git a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java
index 68a0d1a..a07e84a 100644
--- a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java
+++ b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java
@@ -18,17 +18,19 @@
  */
 package org.jclouds.cloudloadbalancers.loadbalancer.functions;
 
-import java.util.Map;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
 import org.jclouds.cloudloadbalancers.domain.LoadBalancer;
 import org.jclouds.cloudloadbalancers.domain.VirtualIP;
+import org.jclouds.collect.Memoized;
 import org.jclouds.domain.Location;
 import org.jclouds.loadbalancer.domain.LoadBalancerMetadata;
 import org.jclouds.loadbalancer.domain.LoadBalancerType;
 import org.jclouds.loadbalancer.domain.internal.LoadBalancerMetadataImpl;
+import org.jclouds.location.predicates.LocationPredicates;
 
 import com.google.common.base.Function;
 import com.google.common.base.Supplier;
@@ -41,20 +43,20 @@
  */
 @Singleton
 public class LoadBalancerToLoadBalancerMetadata implements Function<LoadBalancer, LoadBalancerMetadata> {
-   protected final Supplier<Map<String, ? extends Location>> locationMap;
+   protected final Supplier<Set<? extends Location>> locations;
    protected final Supplier<Location> defaultLocationSupplier;
 
    @Inject
    public LoadBalancerToLoadBalancerMetadata(Supplier<Location> defaultLocationSupplier,
-            Supplier<Map<String, ? extends Location>> locationMap) {
-      this.locationMap = locationMap;
+            @Memoized Supplier<Set<? extends Location>> locations) {
+      this.locations = locations;
       this.defaultLocationSupplier = defaultLocationSupplier;
    }
 
    @Override
    public LoadBalancerMetadata apply(LoadBalancer input) {
 
-      Location location = locationMap.get().get(input.getRegion());
+      Location location = Iterables.find(locations.get(), LocationPredicates.idEquals(input.getRegion()));
 
       String id = input.getRegion() + "/" + input.getId();
       // TODO Builder
diff --git a/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClientTest.java b/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClientTest.java
index 4eb292e..1c1121b 100644
--- a/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClientTest.java
+++ b/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersAsyncClientTest.java
@@ -19,6 +19,11 @@
 package org.jclouds.cloudloadbalancers;
 
 import static org.jclouds.Constants.PROPERTY_API_VERSION;
+import static org.jclouds.Constants.PROPERTY_ENDPOINT;
+import static org.jclouds.cloudloadbalancers.reference.RackspaceConstants.PROPERTY_ACCOUNT_ID;
+import static org.jclouds.cloudloadbalancers.reference.Region.DFW;
+import static org.jclouds.location.reference.LocationConstants.ENDPOINT;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION;
 import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
 
 import java.io.IOException;
@@ -84,9 +89,12 @@
    @Override
    protected Properties getProperties() {
       Properties overrides = new Properties();
-      overrides.setProperty(PROPERTY_REGIONS, "US");
+      overrides.setProperty(PROPERTY_ENDPOINT, "https://auth.api.rackspacecloud.com");
       overrides.setProperty(PROPERTY_API_VERSION, "1");
-      overrides.setProperty(provider + ".endpoint", "https://auth");
+      overrides.setProperty(PROPERTY_REGIONS, "DFW");
+      overrides.setProperty(PROPERTY_REGION + "." + DFW + "." + ENDPOINT, String
+               .format("https://dfw.loadbalancers.api.rackspacecloud.com/v{%s}/{%s}", PROPERTY_API_VERSION,
+                        PROPERTY_ACCOUNT_ID));
       overrides.setProperty(provider + ".contextbuilder", CloudLoadBalancersContextBuilder.class.getName());
       return overrides;
    }
diff --git a/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersAsyncClientTest.java b/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersAsyncClientTest.java
index 2d4ccc8..05280b7 100644
--- a/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersAsyncClientTest.java
+++ b/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersAsyncClientTest.java
@@ -28,22 +28,34 @@
 import org.jclouds.cloudloadbalancers.CloudLoadBalancersAsyncClient;
 import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient;
 import org.jclouds.cloudloadbalancers.config.CloudLoadBalancersRestClientModule;
+import org.jclouds.cloudloadbalancers.functions.ConvertLB;
 import org.jclouds.cloudloadbalancers.reference.Region;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.RequiresHttp;
 import org.jclouds.internal.ClassMethodArgs;
+import org.jclouds.json.config.GsonModule.DateAdapter;
+import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
 import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
+import org.jclouds.openstack.config.OpenStackAuthenticationModule;
 import org.jclouds.openstack.filters.AuthenticateRequest;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.RestClientTest;
+import org.jclouds.rest.RestContext;
 import org.jclouds.rest.RestContextFactory;
 import org.jclouds.rest.RestContextSpec;
+import org.jclouds.rest.internal.RestContextImpl;
 import org.testng.annotations.BeforeClass;
 
 import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.common.base.Throwables;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Module;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.util.Types;
 
 /**
  * @author Adrian Cole
@@ -57,12 +69,43 @@
       protected void bindRegionsToProvider() {
          bindRegionsToProvider(Regions.class);
       }
+      
+      //TODO: replace this with Expect test
+      @SuppressWarnings("unchecked")
+      @Override
+      protected void configure() {
+         // following from CloudLoadBalancersRestClientModule, except we are hard-coding the auth response
+         install(new OpenStackAuthenticationModule() {
+            @Override
+            protected Supplier<AuthenticationResponse> provideAuthenticationResponseSupplier(
+                     final LoadingCache<String,AuthenticationResponse> cache) {
+               return Suppliers.ofInstance(new AuthenticationResponse("token", ImmutableMap.<String, URI> of()));
+            }
+         });
+         bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+         bindRegionsToProvider();
+         install(new FactoryModuleBuilder().build(ConvertLB.Factory.class));
+
+         // following from RestClientModule
+         bind(new TypeLiteral<RestContext>() {
+         }).to(
+               (TypeLiteral) TypeLiteral.get(Types.newParameterizedType(
+                     RestContextImpl.class, syncClientType, asyncClientType))).in(
+               Scopes.SINGLETON);
+         bind(TypeLiteral.get(Types.newParameterizedType(RestContext.class, syncClientType, asyncClientType))).to(
+                  (TypeLiteral) TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class, syncClientType,
+                           asyncClientType))).in(Scopes.SINGLETON);
+         bindAsyncClient();
+         bindClient();
+         bindErrorHandlers();
+         bindRetryHandlers();
+      }
 
       static class Regions implements javax.inject.Provider<Map<String, URI>> {
          @Override
          public Map<String, URI> get() {
-            return ImmutableMap.<String, URI> of("DFW",
-                  URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234"));
+            return ImmutableMap.<String, URI> of("DFW", URI
+                     .create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/1234"));
          }
       }
 
@@ -95,8 +138,8 @@
       super.setupFactory();
       try {
          processor.setCaller(new ClassMethodArgs(CloudLoadBalancersAsyncClient.class,
-               CloudLoadBalancersAsyncClient.class.getMethod("getLoadBalancerClient", String.class),
-               new Object[] { Region.DFW }));
+                  CloudLoadBalancersAsyncClient.class.getMethod("getLoadBalancerClient", String.class),
+                  new Object[] { Region.DFW }));
       } catch (Exception e) {
          Throwables.propagate(e);
       }
diff --git a/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersClientLiveTest.java b/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersClientLiveTest.java
index a673f24..e603547 100644
--- a/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersClientLiveTest.java
+++ b/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/features/BaseCloudLoadBalancersClientLiveTest.java
@@ -18,12 +18,9 @@
  */
 package org.jclouds.cloudloadbalancers.features;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.Constants;
 import org.jclouds.cloudloadbalancers.CloudLoadBalancersAsyncClient;
 import org.jclouds.cloudloadbalancers.CloudLoadBalancersClient;
 import org.jclouds.cloudloadbalancers.domain.LoadBalancer;
@@ -33,6 +30,7 @@
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.net.IPSocket;
 import org.jclouds.predicates.RetryablePredicate;
+import org.jclouds.rest.BaseRestClientLiveTest;
 import org.jclouds.rest.RestContext;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
@@ -47,44 +45,20 @@
  * 
  * @author Adrian Cole
  */
-public class BaseCloudLoadBalancersClientLiveTest {
-   protected String prefix = System.getProperty("user.name");
+public class BaseCloudLoadBalancersClientLiveTest extends BaseRestClientLiveTest {
+   public BaseCloudLoadBalancersClientLiveTest() {
+      provider = "cloudloadbalancers";
+   }
 
    protected CloudLoadBalancersClient client;
    protected RestContext<CloudLoadBalancersClient, CloudLoadBalancersAsyncClient> context;
-   protected String provider = "cloudloadbalancers";
    protected String[] regions = {};
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    protected Predicate<IPSocket> socketTester;
    protected RetryablePredicate<LoadBalancer> loadBalancerActive;
    protected RetryablePredicate<LoadBalancer> loadBalancerDeleted;
 
    protected Injector injector;
 
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider
-               + ".identity must be set.  ex. apiKey");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential must be set.  ex. secretKey");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = "live")
    public void setupClient() {
diff --git a/apis/cloudservers/pom.xml b/apis/cloudservers/pom.xml
index f9d6002..9abeb16 100644
--- a/apis/cloudservers/pom.xml
+++ b/apis/cloudservers/pom.xml
@@ -36,7 +36,8 @@
 
   <properties>
     <test.cloudservers.endpoint>https://auth.api.rackspacecloud.com</test.cloudservers.endpoint>
-    <test.cloudservers.apiversion>1.0</test.cloudservers.apiversion>
+    <test.cloudservers.api-version>1.0</test.cloudservers.api-version>
+    <test.cloudservers.build-version></test.cloudservers.build-version>
     <test.cloudservers.identity>${test.rackspace.identity}</test.cloudservers.identity>
     <test.cloudservers.credential>${test.rackspace.credential}</test.cloudservers.credential>
     <test.cloudservers.image-id />
@@ -108,7 +109,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.cloudstack.endpoint>${test.cloudstack.endpoint}</test.cloudstack.endpoint>
-                    <test.cloudstack.apiversion>${test.cloudstack.apiversion}</test.cloudstack.apiversion>
+                    <test.cloudstack.api-version>${test.cloudstack.api-version}</test.cloudstack.api-version>
+                    <test.cloudstack.build-version>${test.cloudstack.build-version}</test.cloudstack.build-version>
                     <test.cloudstack.identity>${test.cloudstack.identity}</test.cloudstack.identity>
                     <test.cloudstack.credential>${test.cloudstack.credential}</test.cloudstack.credential>
                     <test.cloudstack.image-id>${test.cloudstack.image-id}</test.cloudstack.image-id>
diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/config/CloudServersRestClientModule.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/config/CloudServersRestClientModule.java
index c643ce3..7340bb7 100644
--- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/config/CloudServersRestClientModule.java
+++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/config/CloudServersRestClientModule.java
@@ -27,6 +27,7 @@
 import org.jclouds.cloudservers.ServerManagement;
 import org.jclouds.cloudservers.handlers.ParseCloudServersErrorFromHttpResponse;
 import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpRetryHandler;
 import org.jclouds.http.RequiresHttp;
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
@@ -35,6 +36,7 @@
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
 import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
 import org.jclouds.openstack.config.OpenStackAuthenticationModule;
+import org.jclouds.openstack.handlers.RetryOnRenew;
 import org.jclouds.openstack.reference.AuthHeaders;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.config.RestClientModule;
@@ -74,6 +76,11 @@
       bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseCloudServersErrorFromHttpResponse.class);
    }
 
+   @Override
+   protected void bindRetryHandlers() {
+      bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class);
+   }
+
    @Provides
    @Singleton
    @ServerManagement
diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/CloudServersExpectTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/CloudServersExpectTest.java
new file mode 100644
index 0000000..7c7a2ae
--- /dev/null
+++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/CloudServersExpectTest.java
@@ -0,0 +1,81 @@
+/**
+ * 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.cloudservers;
+
+import java.net.URI;
+
+import org.jclouds.cloudservers.internal.BaseCloudServersRestClientExpectTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "CloudServersExpectTest")
+public class CloudServersExpectTest extends BaseCloudServersRestClientExpectTest {
+   
+   HttpRequest initialAuth = HttpRequest.builder().method("GET").endpoint(URI.create("https://auth/v1.0"))
+            .headers(
+            ImmutableMultimap.<String, String> builder()
+            .put("X-Auth-User", "identity")
+            .put("X-Auth-Key", "credential")
+            .put("Accept", "*/*").build()).build();
+   
+   String authToken = "d6245d35-22a0-47c0-9770-2c5097da25fc";
+   
+   HttpResponse responseWithUrls = HttpResponse.builder().statusCode(204).message("HTTP/1.1 204 No Content")
+            .headers(ImmutableMultimap.<String,String>builder()
+            .put("Server", "Apache/2.2.3 (Red Hat)")
+            .put("vary", "X-Auth-Token,X-Auth-Key,X-Storage-User,X-Storage-Pass")
+            .put("X-Storage-Url", "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")
+            .put("Cache-Control", "s-maxage=86399")
+            .put("Content-Type", "text/xml")
+            .put("Date", "Tue, 10 Jan 2012 22:08:47 GMT")
+            .put("X-Auth-Token", authToken)
+            .put("X-Server-Management-Url","https://servers.api.rackspacecloud.com/v1.0/413274")
+            .put("X-Storage-Token", authToken)
+            .put("Connection", "Keep-Alive")
+            .put("X-CDN-Management-Url", "https://cdn1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")
+            .put("Content-Length", "0")
+            .build()).build();
+   
+   public void deleteImageReturnsTrueOn200AndFalseOn404() {
+      
+      HttpRequest deleteImage11 = HttpRequest.builder().method("DELETE").endpoint(
+               URI.create("https://servers.api.rackspacecloud.com/v1.0/413274/images/11?now=1257695648897")).headers(
+               ImmutableMultimap.<String, String> builder()
+               .put("X-Auth-Token", authToken).build()).build();
+      
+      HttpResponse imageDeleted = HttpResponse.builder().statusCode(204).message("HTTP/1.1 204 No Content").build();
+
+      CloudServersClient clientWhenImageExists = requestsSendResponses(initialAuth, responseWithUrls, deleteImage11, imageDeleted);
+      assert clientWhenImageExists.deleteImage(11);
+
+      HttpResponse imageNotFound = HttpResponse.builder().statusCode(404).message("HTTP/1.1 404 Not Found").build();
+
+      CloudServersClient clientWhenImageDoesntExist =  requestsSendResponses(initialAuth, responseWithUrls, deleteImage11, imageNotFound);
+      assert !clientWhenImageDoesntExist.deleteImage(11);
+      
+   }
+
+}
\ No newline at end of file
diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/handlers/RetryOnRenewExpectTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/handlers/RetryOnRenewExpectTest.java
new file mode 100644
index 0000000..0192555
--- /dev/null
+++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/handlers/RetryOnRenewExpectTest.java
@@ -0,0 +1,173 @@
+/**
+ * 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.cloudservers.handlers;
+
+import java.net.URI;
+
+import org.jclouds.cloudservers.CloudServersClient;
+import org.jclouds.cloudservers.internal.BaseCloudServersRestClientExpectTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.io.Payloads;
+import org.jclouds.rest.AuthorizationException;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * Tests behavior of {@code RetryOnRenew} handler
+ * 
+ * @author grkvlt@apache.org
+ */
+@Test(groups = "unit", testName = "RetryOnRenewExpectTest")
+public class RetryOnRenewExpectTest extends BaseCloudServersRestClientExpectTest {
+
+   @Test
+   public void testShouldReauthenticateOn401() {
+      String authToken = "d6245d35-22a0-47c0-9770-2c5097da25fc";
+      String authToken2 = "12345678-9012-47c0-9770-2c5097da25fc";
+      
+      HttpRequest initialAuth = HttpRequest.builder().method("GET").endpoint(URI.create("https://auth/v1.0"))
+               .headers(
+               ImmutableMultimap.<String, String> builder()
+               .put("X-Auth-User", "identity")
+               .put("X-Auth-Key", "credential")
+               .put("Accept", "*/*").build()).build();
+      
+      
+      HttpResponse responseWithUrls = HttpResponse.builder().statusCode(204).message("HTTP/1.1 204 No Content")
+               .headers(ImmutableMultimap.<String,String>builder()
+               .put("Server", "Apache/2.2.3 (Red Hat)")
+               .put("vary", "X-Auth-Token,X-Auth-Key,X-Storage-User,X-Storage-Pass")
+               .put("X-Storage-Url", "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")
+               .put("Cache-Control", "s-maxage=86399")
+               .put("Content-Type", "text/xml")
+               .put("Date", "Tue, 10 Jan 2012 22:08:47 GMT")
+               .put("X-Auth-Token", authToken)
+               .put("X-Server-Management-Url","https://servers.api.rackspacecloud.com/v1.0/413274")
+               .put("X-Storage-Token", authToken)
+               .put("Connection", "Keep-Alive")
+               .put("X-CDN-Management-Url", "https://cdn1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")
+               .put("Content-Length", "0")
+               .build()).build();
+      
+      HttpResponse responseWithUrls2 = HttpResponse.builder().statusCode(204).message("HTTP/1.1 204 No Content")
+               .headers(ImmutableMultimap.<String,String>builder()
+               .put("Server", "Apache/2.2.3 (Red Hat)")
+               .put("vary", "X-Auth-Token,X-Auth-Key,X-Storage-User,X-Storage-Pass")
+               .put("X-Storage-Url", "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")
+               .put("Cache-Control", "s-maxage=86399")
+               .put("Content-Type", "text/xml")
+               .put("Date", "Tue, 10 Jan 2012 22:08:47 GMT")
+               .put("X-Auth-Token", authToken2)
+               .put("X-Server-Management-Url","https://servers.api.rackspacecloud.com/v1.0/413274")
+               .put("X-Storage-Token", authToken2)
+               .put("Connection", "Keep-Alive")
+               .put("X-CDN-Management-Url", "https://cdn1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")
+               .put("Content-Length", "0")
+               .build()).build();
+                     
+      HttpRequest deleteImage = HttpRequest.builder().method("DELETE").endpoint(
+               URI.create("https://servers.api.rackspacecloud.com/v1.0/413274/images/11?now=1257695648897")).headers(
+               ImmutableMultimap.<String, String> builder()
+               .put("X-Auth-Token", authToken).build()).build();
+
+      HttpResponse pleaseRenew = HttpResponse.builder().statusCode(401)
+               .message("HTTP/1.1 401 Unauthorized")
+               .payload(Payloads.newStringPayload("[{\"unauthorized\":{\"message\":\"Invalid authentication token.  Please renew.\",\"code\":401}}]"))
+               .build();
+
+      HttpRequest deleteImage2 = HttpRequest.builder().method("DELETE").endpoint(
+               URI.create("https://servers.api.rackspacecloud.com/v1.0/413274/images/11?now=1257695648897")).headers(
+               ImmutableMultimap.<String, String> builder()
+               .put("X-Auth-Token", authToken2).build()).build();
+
+      HttpResponse imageDeleted = HttpResponse.builder().statusCode(204).message("HTTP/1.1 204 No Content").build();
+
+      CloudServersClient clientWhenImageExists = orderedRequestsSendResponses(initialAuth, responseWithUrls,
+                deleteImage, pleaseRenew, initialAuth, responseWithUrls2, deleteImage2, imageDeleted);
+      
+      assert clientWhenImageExists.deleteImage(11);
+   }
+
+   @Test(expectedExceptions=AuthorizationException.class)
+   public void testDoesNotReauthenticateOnFatal401() {
+      String authToken = "d6245d35-22a0-47c0-9770-2c5097da25fc";
+      
+      HttpRequest initialAuth = HttpRequest.builder().method("GET").endpoint(URI.create("https://auth/v1.0"))
+               .headers(
+               ImmutableMultimap.<String, String> builder()
+               .put("X-Auth-User", "identity")
+               .put("X-Auth-Key", "credential")
+               .put("Accept", "*/*").build()).build();
+      
+      
+      HttpResponse responseWithUrls = HttpResponse.builder().statusCode(204).message("HTTP/1.1 204 No Content")
+               .headers(ImmutableMultimap.<String,String>builder()
+               .put("Server", "Apache/2.2.3 (Red Hat)")
+               .put("vary", "X-Auth-Token,X-Auth-Key,X-Storage-User,X-Storage-Pass")
+               .put("X-Storage-Url", "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")
+               .put("Cache-Control", "s-maxage=86399")
+               .put("Content-Type", "text/xml")
+               .put("Date", "Tue, 10 Jan 2012 22:08:47 GMT")
+               .put("X-Auth-Token", authToken)
+               .put("X-Server-Management-Url","https://servers.api.rackspacecloud.com/v1.0/413274")
+               .put("X-Storage-Token", authToken)
+               .put("Connection", "Keep-Alive")
+               .put("X-CDN-Management-Url", "https://cdn1.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22")
+               .put("Content-Length", "0")
+               .build()).build();
+      
+      HttpRequest deleteImage = HttpRequest.builder().method("DELETE").endpoint(
+               URI.create("https://servers.api.rackspacecloud.com/v1.0/413274/images/11?now=1257695648897")).headers(
+               ImmutableMultimap.<String, String> builder()
+               .put("X-Auth-Token", authToken).build()).build();
+
+      HttpResponse unauthResponse = HttpResponse.builder().statusCode(401)
+               .message("HTTP/1.1 401 Unauthorized")
+               .payload(Payloads.newStringPayload("[{\"unauthorized\":{\"message\":\"Fatal unauthorized.\",\"code\":401}}]"))
+               .build();
+
+      CloudServersClient client = orderedRequestsSendResponses(initialAuth, responseWithUrls,
+                deleteImage, unauthResponse);
+      
+      client.deleteImage(11);
+   }
+
+   // FIXME stack trace shows the AuthorizationException, but it's buried inside a guice TestException
+   @Test(enabled=false, expectedExceptions=AuthorizationException.class)
+   public void testDoesNotReauthenticateOnAuthentication401() {
+      HttpRequest initialAuth = HttpRequest.builder().method("GET").endpoint(URI.create("https://auth/v1.0"))
+               .headers(
+               ImmutableMultimap.<String, String> builder()
+               .put("X-Auth-User", "identity")
+               .put("X-Auth-Key", "credential")
+               .put("Accept", "*/*").build()).build();
+      
+      
+      HttpResponse unauthResponse = HttpResponse.builder().statusCode(401)
+               .message("HTTP/1.1 401 Unauthorized")
+               .payload(Payloads.newStringPayload("[{\"unauthorized\":{\"message\":\"A different message implying fatal.\",\"code\":401}}]"))
+               .build();
+
+      CloudServersClient client = orderedRequestsSendResponses(initialAuth, unauthResponse);
+                    
+      client.deleteImage(11);
+   }
+}
diff --git a/apis/cloudservers/src/test/java/org/jclouds/cloudservers/internal/BaseCloudServersRestClientExpectTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/internal/BaseCloudServersRestClientExpectTest.java
new file mode 100644
index 0000000..94050b8
--- /dev/null
+++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/internal/BaseCloudServersRestClientExpectTest.java
@@ -0,0 +1,96 @@
+/**
+ * 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.cloudservers.internal;
+
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
+
+import java.util.Date;
+import java.util.Properties;
+
+import org.jclouds.cloudservers.CloudServersClient;
+import org.jclouds.cloudservers.CloudServersContextBuilder;
+import org.jclouds.cloudservers.CloudServersPropertiesBuilder;
+import org.jclouds.cloudservers.config.CloudServersRestClientModule;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.http.RequiresHttp;
+import org.jclouds.openstack.config.OpenStackAuthenticationModule;
+import org.jclouds.openstack.filters.AddTimestampQuery;
+import org.jclouds.rest.BaseRestClientExpectTest;
+import org.jclouds.rest.ConfiguresRestClient;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Module;
+
+/**
+ * Base class for writing CloudServers Rest Client Expect tests
+ * 
+ * @author Adrian Cole
+ */
+public class BaseCloudServersRestClientExpectTest extends BaseRestClientExpectTest<CloudServersClient> {
+
+   public BaseCloudServersRestClientExpectTest() {
+      provider = "cloudservers";
+   }
+
+   @Override
+   protected Properties setupRestProperties() {
+      Properties overrides = new Properties();
+      overrides.setProperty(PROPERTY_REGIONS, "US");
+      overrides.setProperty(provider + ".endpoint", "https://auth");
+      overrides.setProperty(provider + ".contextbuilder", CloudServersContextBuilder.class.getName());
+      overrides.setProperty(provider + ".propertiesbuilder", CloudServersPropertiesBuilder.class.getName());
+      return overrides;
+   }
+
+   protected static final String CONSTANT_DATE = "2009-11-08T15:54:08.897Z";
+
+   /**
+    * override so that we can control the timestamp used in {@link AddTimestampQuery}
+    */
+   static class TestOpenStackAuthenticationModule extends OpenStackAuthenticationModule {
+      @Override
+      protected void configure() {
+         super.configure();
+      }
+
+      @Override
+      public Supplier<Date> provideCacheBusterDate() {
+         return new Supplier<Date>() {
+            public Date get() {
+               return new SimpleDateFormatDateService().iso8601DateParse(CONSTANT_DATE);
+            }
+         };
+      }
+
+   }
+
+   @Override
+   protected Module createModule() {
+      return new TestCloudServersRestClientModule();
+   }
+
+   @ConfiguresRestClient
+   @RequiresHttp
+   protected static class TestCloudServersRestClientModule extends CloudServersRestClientModule {
+      private TestCloudServersRestClientModule() {
+         super(new TestOpenStackAuthenticationModule());
+      }
+
+   }
+}
diff --git a/apis/cloudsigma/pom.xml b/apis/cloudsigma/pom.xml
index 6e491d9..c2cc9a2 100644
--- a/apis/cloudsigma/pom.xml
+++ b/apis/cloudsigma/pom.xml
@@ -35,12 +35,13 @@
   
   <properties>
     <test.cloudsigma.endpoint>https://api.cloudsigma.com</test.cloudsigma.endpoint>
-    <test.cloudsigma.apiversion>1.0</test.cloudsigma.apiversion>
+    <test.cloudsigma.api-version>1.0</test.cloudsigma.api-version>
+    <test.cloudsigma.build-version></test.cloudsigma.build-version>
     <test.cloudsigma.identity>FIXME</test.cloudsigma.identity>
     <test.cloudsigma.credential>FIXME</test.cloudsigma.credential>
-    <test.cloudsigma.image-id />
-    <test.cloudsigma.image.login-user />
-    <test.cloudsigma.image.authenticate-sudo />
+    <test.cloudsigma.image-id>f3c7c665-cd54-4a78-8fd2-7ec2f028cf29</test.cloudsigma.image-id>
+    <test.cloudsigma.image.login-user></test.cloudsigma.image.login-user>
+    <test.cloudsigma.image.authenticate-sudo></test.cloudsigma.image.authenticate-sudo>
   </properties>
 
   <dependencies>
@@ -94,7 +95,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.cloudsigma.endpoint>${test.cloudsigma.endpoint}</test.cloudsigma.endpoint>
-                    <test.cloudsigma.apiversion>${test.cloudsigma.apiversion}</test.cloudsigma.apiversion>
+                    <test.cloudsigma.api-version>${test.cloudsigma.api-version}</test.cloudsigma.api-version>
+                    <test.cloudsigma.build-version>${test.cloudsigma.build-version}</test.cloudsigma.build-version>
                     <test.cloudsigma.identity>${test.cloudsigma.identity}</test.cloudsigma.identity>
                     <test.cloudsigma.credential>${test.cloudsigma.credential}</test.cloudsigma.credential>
                     <test.cloudsigma.image-id>${test.cloudsigma.image-id}</test.cloudsigma.image-id>
diff --git a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java
index 9d0cd92..babe31d 100644
--- a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java
+++ b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.cloudsigma;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
@@ -28,7 +27,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
 
-import org.jclouds.Constants;
 import org.jclouds.cloudsigma.domain.ClaimType;
 import org.jclouds.cloudsigma.domain.CreateDriveRequest;
 import org.jclouds.cloudsigma.domain.DriveData;
@@ -46,6 +44,8 @@
 import org.jclouds.cloudsigma.options.CloneDriveOptions;
 import org.jclouds.cloudsigma.predicates.DriveClaimed;
 import org.jclouds.cloudsigma.util.Servers;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
+import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.domain.LoginCredentials;
@@ -62,6 +62,7 @@
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.gson.Gson;
@@ -73,56 +74,39 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", singleThreaded = true)
-public class CloudSigmaClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "CloudSigmaClientLiveTest")
+public class CloudSigmaClientLiveTest extends BaseVersionedServiceLiveTest {
+   public CloudSigmaClientLiveTest() {
+      provider = "cloudsigma";
+   }
+   
    protected long driveSize = 8 * 1024 * 1024 * 1024l;
    protected int maxDriveImageTime = 300;
    protected String vncPassword = "Il0veVNC";
-   protected String bootDrive = "f3c7c665-cd54-4a78-8fd2-7ec2f028cf29";
    protected CloudSigmaClient client;
    protected RestContext<CloudSigmaClient, CloudSigmaAsyncClient> context;
    protected Predicate<IPSocket> socketTester;
 
-   protected String provider = "cloudsigma";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    protected Predicate<DriveInfo> driveNotClaimed;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
+   protected ComputeServiceContext computeContext;
 
    @BeforeGroups(groups = "live")
    public void setupClient() {
       setupCredentials();
       Properties overrides = setupProperties();
-      context = new ComputeServiceContextFactory().createContext(provider,
-            ImmutableSet.<Module> of(new Log4JLoggingModule()), overrides).getProviderSpecificContext();
-
+      computeContext = new ComputeServiceContextFactory().createContext(provider,
+            ImmutableSet.<Module> of(new Log4JLoggingModule()), overrides);
+      context = computeContext.getProviderSpecificContext();
+      
       client = context.getApi();
       driveNotClaimed = new RetryablePredicate<DriveInfo>(Predicates.not(new DriveClaimed(client)), maxDriveImageTime,
             1, TimeUnit.SECONDS);
       socketTester = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), maxDriveImageTime, 1,
             TimeUnit.SECONDS);
+      
+      if (Strings.emptyToNull(imageId) == null) {
+         imageId = computeContext.getComputeService().templateBuilder().build().getImage().getId();
+      }
    }
 
    @Test
@@ -459,7 +443,7 @@
 
    protected void prepareDrive() {
       client.destroyDrive(drive.getUuid());
-      drive = client.cloneDrive(bootDrive, drive.getName(),
+      drive = client.cloneDrive(imageId, drive.getName(),
             new CloneDriveOptions()
             .size(driveSize)
             .tags("cat:mouse", "monkey:banana")
diff --git a/apis/cloudstack/pom.xml b/apis/cloudstack/pom.xml
index fcc5ee1..090b0e9 100644
--- a/apis/cloudstack/pom.xml
+++ b/apis/cloudstack/pom.xml
@@ -49,7 +49,8 @@
   
   <properties>
     <test.cloudstack.endpoint>http://localhost:8080/client/api</test.cloudstack.endpoint>
-    <test.cloudstack.apiversion>2.2.12</test.cloudstack.apiversion>
+    <test.cloudstack.api-version>2.2.12</test.cloudstack.api-version>
+    <test.cloudstack.build-version></test.cloudstack.build-version>
     <test.cloudstack.identity>FIXME_apiKey</test.cloudstack.identity>
     <test.cloudstack.credential>FIXME_secretKey</test.cloudstack.credential>
     <test.cloudstack.domainAdminIdentity />
@@ -116,7 +117,8 @@
                   <threadCount>2</threadCount>
                   <systemPropertyVariables>
                     <test.cloudstack.endpoint>${test.cloudstack.endpoint}</test.cloudstack.endpoint>
-                    <test.cloudstack.apiversion>${test.cloudstack.apiversion}</test.cloudstack.apiversion>
+                    <test.cloudstack.api-version>${test.cloudstack.api-version}</test.cloudstack.api-version>
+                    <test.cloudstack.build-version>${test.cloudstack.build-version}</test.cloudstack.build-version>
                     <test.cloudstack.identity>${test.cloudstack.identity}</test.cloudstack.identity>
                     <test.cloudstack.credential>${test.cloudstack.credential}</test.cloudstack.credential>
                     <test.cloudstack.image-id>${test.cloudstack.image-id}</test.cloudstack.image-id>
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackContext.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackContext.java
index 25dcb71..1eb716f 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackContext.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackContext.java
@@ -1,3 +1,21 @@
+/**
+ * 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.cloudstack;
 
 import org.jclouds.cloudstack.internal.CloudStackContextImpl;
@@ -15,8 +33,12 @@
 @ImplementedBy(CloudStackContextImpl.class)
 public interface CloudStackContext extends ComputeServiceContext {
 
-    RestContext<CloudStackDomainClient, CloudStackDomainAsyncClient> getDomainContext();
-    
-    RestContext<CloudStackGlobalClient, CloudStackGlobalAsyncClient> getGlobalContext();
+   @SuppressWarnings("unchecked")
+   @Override
+   RestContext<CloudStackClient, CloudStackAsyncClient> getProviderSpecificContext();
+
+   RestContext<CloudStackDomainClient, CloudStackDomainAsyncClient> getDomainContext();
+
+   RestContext<CloudStackGlobalClient, CloudStackGlobalAsyncClient> getGlobalContext();
 
 }
\ No newline at end of file
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalAsyncClient.java
index 6773098..b73d74a 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalAsyncClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalAsyncClient.java
@@ -21,6 +21,8 @@
 import org.jclouds.cloudstack.features.GlobalAccountAsyncClient;
 import org.jclouds.cloudstack.features.GlobalAlertAsyncClient;
 import org.jclouds.cloudstack.features.GlobalCapacityAsyncClient;
+import org.jclouds.cloudstack.features.GlobalConfigurationAsyncClient;
+import org.jclouds.cloudstack.features.GlobalConfigurationClient;
 import org.jclouds.cloudstack.features.GlobalHostAsyncClient;
 import org.jclouds.cloudstack.features.GlobalOfferingAsyncClient;
 import org.jclouds.cloudstack.features.GlobalStoragePoolAsyncClient;
@@ -90,4 +92,11 @@
     */
    @Delegate
    GlobalUsageAsyncClient getUsageClient();
+
+   /**
+    * Provides asynchronous access to Configuration
+    */
+   @Delegate
+   @Override
+   GlobalConfigurationAsyncClient getConfigurationClient();
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalClient.java
index a1f7367..b6dc916 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackGlobalClient.java
@@ -23,6 +23,7 @@
 import org.jclouds.cloudstack.features.GlobalAccountClient;
 import org.jclouds.cloudstack.features.GlobalAlertClient;
 import org.jclouds.cloudstack.features.GlobalCapacityClient;
+import org.jclouds.cloudstack.features.GlobalConfigurationClient;
 import org.jclouds.cloudstack.features.GlobalHostClient;
 import org.jclouds.cloudstack.features.GlobalOfferingClient;
 import org.jclouds.cloudstack.features.GlobalStoragePoolClient;
@@ -94,4 +95,11 @@
     */
    @Delegate
    GlobalUsageClient getUsageClient();
+
+   /**
+    * Provides synchronous access to Configuration
+    */
+   @Delegate
+   @Override
+   GlobalConfigurationClient getConfigurationClient();
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackPropertiesBuilder.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackPropertiesBuilder.java
index 0d0d77b..0238bc0 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackPropertiesBuilder.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackPropertiesBuilder.java
@@ -19,6 +19,7 @@
 package org.jclouds.cloudstack;
 
 import static org.jclouds.Constants.PROPERTY_API_VERSION;
+import static org.jclouds.Constants.PROPERTY_ENDPOINT;
 
 import java.util.Properties;
 
@@ -33,6 +34,7 @@
    @Override
    protected Properties defaultProperties() {
       Properties properties = super.defaultProperties();
+      properties.setProperty(PROPERTY_ENDPOINT, "http://localhost:8080/client/api");
       properties.setProperty(PROPERTY_API_VERSION, "2.2");
       properties.setProperty("jclouds.ssh.max-retries", "7");
       properties.setProperty("jclouds.ssh.retry-auth", "true");
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
index 8869ef3..fbf3193 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
@@ -29,6 +29,7 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import org.jclouds.cloudstack.CloudStackAsyncClient;
 import org.jclouds.cloudstack.CloudStackClient;
@@ -38,9 +39,13 @@
 import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata;
 import org.jclouds.cloudstack.compute.functions.ZoneToLocation;
 import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.compute.strategy.AdvancedNetworkOptionsConverter;
+import org.jclouds.cloudstack.compute.strategy.BasicNetworkOptionsConverter;
 import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter;
+import org.jclouds.cloudstack.compute.strategy.OptionsConverter;
 import org.jclouds.cloudstack.domain.IPForwardingRule;
 import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.domain.NetworkType;
 import org.jclouds.cloudstack.domain.OSType;
 import org.jclouds.cloudstack.domain.ServiceOffering;
 import org.jclouds.cloudstack.domain.Template;
@@ -49,9 +54,11 @@
 import org.jclouds.cloudstack.domain.Zone;
 import org.jclouds.cloudstack.features.GuestOSClient;
 import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
+import org.jclouds.cloudstack.functions.ZoneIdToZone;
 import org.jclouds.cloudstack.predicates.JobComplete;
 import org.jclouds.cloudstack.suppliers.GetCurrentUser;
 import org.jclouds.cloudstack.suppliers.NetworksForCurrentUser;
+import org.jclouds.cloudstack.suppliers.ZoneIdToZoneSupplier;
 import org.jclouds.collect.Memoized;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
@@ -108,6 +115,10 @@
       install(new FactoryModuleBuilder().build(StaticNATVirtualMachineInNetwork.Factory.class));
       bind(new TypeLiteral<CacheLoader<Long, Set<IPForwardingRule>>>() {
       }).to(GetIPForwardingRulesByVirtualMachine.class);
+      bind(new TypeLiteral<CacheLoader<Long, Zone>>() {
+      }).to(ZoneIdToZone.class);
+      bind(new TypeLiteral<Supplier<LoadingCache<Long, Zone>>>() {
+      }).to(ZoneIdToZoneSupplier.class);
    }
 
    @Provides
@@ -198,4 +209,11 @@
       }
    }
 
+   @Provides
+   @Singleton
+   Map<NetworkType, ? extends OptionsConverter> optionsConverters(){
+      return ImmutableMap.of(
+         NetworkType.ADVANCED, new AdvancedNetworkOptionsConverter(),
+         NetworkType.BASIC, new BasicNetworkOptionsConverter());
+   }
 }
\ No newline at end of file
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystem.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystem.java
index a98fcbd..6880782 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystem.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystem.java
@@ -1,3 +1,21 @@
+/**
+ * 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.cloudstack.compute.functions;
 
 import static com.google.common.base.Preconditions.checkNotNull;
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
new file mode 100644
index 0000000..d8aa4d2
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
@@ -0,0 +1,59 @@
+/**
+ * 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.cloudstack.compute.strategy;
+
+import com.google.common.collect.Iterables;
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
+
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Predicates.and;
+import static com.google.common.collect.Iterables.filter;
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.defaultNetworkInZone;
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.supportsStaticNAT;
+
+/**
+ * Convert template options into DeployVirtualMachineOptions, when the target zone has advanced networking.
+ *
+ * @author Richard Downer
+ */
+public class AdvancedNetworkOptionsConverter implements OptionsConverter {
+   @Override
+   public DeployVirtualMachineOptions apply(CloudStackTemplateOptions templateOptions, Map<Long, Network> networks, long zoneId, DeployVirtualMachineOptions options) {
+      // security groups not allowed.
+      // at least one network must be given to CloudStack,
+      // but jclouds will try to autodetect an appropriate network if none given.
+      checkArgument(templateOptions.getSecurityGroupIds().isEmpty(), "security groups cannot be specified for locations (zones) that use advanced networking");
+      if (templateOptions.getNetworkIds().size() > 0) {
+         options.networkIds(templateOptions.getNetworkIds());
+      } else {
+         checkArgument(!networks.isEmpty(), "please setup a network for zone: " + zoneId);
+         Network defaultNetworkInZone = Iterables.getFirst(filter(networks.values(), and(defaultNetworkInZone(zoneId), supportsStaticNAT())), null);
+         if(defaultNetworkInZone == null) {
+            throw new IllegalArgumentException("please choose a specific network in zone " + zoneId + ": " + networks);
+         } else {
+            options.networkId(defaultNetworkInZone.getId());
+         }
+      }
+      return options;
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java
new file mode 100644
index 0000000..072de8a
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java
@@ -0,0 +1,45 @@
+/**
+ * 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.cloudstack.compute.strategy;
+
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
+
+import java.util.Map;
+
+/**
+ * Convert template options into DeployVirtualMachineOptions, when the target zone has basic networking.
+ *
+ * @author Richard Downer
+ */
+public class BasicNetworkOptionsConverter implements OptionsConverter {
+   @Override
+   public DeployVirtualMachineOptions apply(CloudStackTemplateOptions templateOptions, Map<Long, Network> networks, long zoneId, DeployVirtualMachineOptions options) {
+      // both security groups and networks are optional, and CloudStack will
+      // use the zone/user's default network/security group if none given
+      if (templateOptions.getSecurityGroupIds().size() > 0) {
+         options.securityGroupIds(templateOptions.getSecurityGroupIds());
+      }
+      if (templateOptions.getNetworkIds().size() > 0) {
+         options.networkIds(templateOptions.getNetworkIds());
+      }
+      return options;
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
index c84b6c9..64f8325 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java
@@ -20,27 +20,33 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Predicates.and;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static org.jclouds.cloudstack.options.DeployVirtualMachineOptions.Builder.displayName;
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.defaultNetworkInZone;
 import static org.jclouds.cloudstack.predicates.NetworkPredicates.supportsStaticNAT;
 import static org.jclouds.cloudstack.predicates.TemplatePredicates.isReady;
 
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
 
 import javax.annotation.Resource;
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import com.google.common.base.Throwables;
+import com.google.common.collect.Iterables;
 import org.jclouds.cloudstack.CloudStackClient;
 import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.IPForwardingRule;
 import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.domain.NetworkType;
 import org.jclouds.cloudstack.domain.PublicIPAddress;
 import org.jclouds.cloudstack.domain.ServiceOffering;
 import org.jclouds.cloudstack.domain.Template;
@@ -88,6 +94,8 @@
    private final CreatePortForwardingRulesForIP setupPortForwardingRulesForIP;
    private final LoadingCache<Long, Set<IPForwardingRule>> vmToRules;
    private final Map<String, Credentials> credentialStore;
+   private final Map<NetworkType, ? extends OptionsConverter> optionsConverters;
+   private final Supplier<LoadingCache<Long, Zone>> zoneIdToZone;
 
    @Inject
    public CloudStackComputeServiceAdapter(CloudStackClient client, Predicate<Long> jobComplete,
@@ -95,7 +103,8 @@
          BlockUntilJobCompletesAndReturnResult blockUntilJobCompletesAndReturnResult,
          StaticNATVirtualMachineInNetwork.Factory staticNATVMInNetwork,
          CreatePortForwardingRulesForIP setupPortForwardingRulesForIP, LoadingCache<Long, Set<IPForwardingRule>> vmToRules,
-         Map<String, Credentials> credentialStore) {
+         Map<String, Credentials> credentialStore, Map<NetworkType, ? extends OptionsConverter> optionsConverters,
+         Supplier<LoadingCache<Long, Zone>> zoneIdToZone) {
       this.client = checkNotNull(client, "client");
       this.jobComplete = checkNotNull(jobComplete, "jobComplete");
       this.networkSupplier = checkNotNull(networkSupplier, "networkSupplier");
@@ -105,6 +114,8 @@
       this.setupPortForwardingRulesForIP = checkNotNull(setupPortForwardingRulesForIP, "setupPortForwardingRulesForIP");
       this.vmToRules = checkNotNull(vmToRules, "vmToRules");
       this.credentialStore = checkNotNull(credentialStore, "credentialStore");
+      this.optionsConverters = optionsConverters;
+      this.zoneIdToZone = zoneIdToZone;
    }
 
    @Override
@@ -118,30 +129,19 @@
       Map<Long, Network> networks = networkSupplier.get();
 
       final long zoneId = Long.parseLong(template.getLocation().getId());
+      Zone zone = null;
+      try {
+         zone = zoneIdToZone.get().get(zoneId);
+      } catch (ExecutionException e) {
+         Throwables.propagate(e);
+      }
 
       CloudStackTemplateOptions templateOptions = template.getOptions().as(CloudStackTemplateOptions.class);
 
-      DeployVirtualMachineOptions options = displayName(name).name(name);
-      if (templateOptions.getSecurityGroupIds().size() > 0) {
-         options.securityGroupIds(templateOptions.getSecurityGroupIds());
-      } else if (templateOptions.getNetworkIds().size() > 0) {
-         options.networkIds(templateOptions.getNetworkIds());
-      } else if (networks.size() > 0) {
-         try {
-            options.networkId(getOnlyElement(filter(networks.values(), and(new Predicate<Network>() {
-
-               @Override
-               public boolean apply(Network arg0) {
-                  return arg0.getZoneId() == zoneId && arg0.isDefault();
-               }
-
-            }, supportsStaticNAT()))).getId());
-         } catch (IllegalArgumentException e) {
-            throw new IllegalArgumentException("please choose a specific network in zone " + zoneId + ": " + networks);
-         }
-      } else {
-         throw new IllegalArgumentException("please setup a network or security group for zone: " + zoneId);
-      }
+      checkState(optionsConverters.containsKey(zone.getNetworkType()), "no options converter configured for network type %s",zone.getNetworkType());
+      DeployVirtualMachineOptions options = DeployVirtualMachineOptions.Builder.displayName(name).name(name);
+      OptionsConverter optionsConverter = optionsConverters.get(zone.getNetworkType());
+      options = optionsConverter.apply(templateOptions, networks, zoneId, options);
 
       if (templateOptions.getIpOnDefaultNetwork() != null) {
          options.ipOnDefaultNetwork(templateOptions.getIpOnDefaultNetwork());
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java
new file mode 100644
index 0000000..bbd0cf5
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java
@@ -0,0 +1,54 @@
+/**
+ * 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.cloudstack.compute.strategy;
+
+import com.google.common.collect.Iterables;
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
+
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Predicates.and;
+import static com.google.common.collect.Iterables.filter;
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.defaultNetworkInZone;
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.supportsStaticNAT;
+
+/**
+ * Convert template options into DeployVirtualMachineOptions. Expressed as an interface, because in
+ * CloudStack different zone network types have different requirements when it comes to networks and
+ * security groups.
+ *
+ * @author Richard Downer
+ */
+public interface OptionsConverter {
+
+   /**
+    * Convert a CloudStackTemplateOptions and apply to a DeployVirtualMachineOptions instance.
+    *
+    * @param templateOptions the input set of options
+    * @param networks the networks available
+    * @param zoneId the zone of the new virtual machine
+    * @param options where the resulting set of options will be applied
+    * @return same as "options" parameter
+    */
+   DeployVirtualMachineOptions apply(CloudStackTemplateOptions templateOptions, Map<Long, Network> networks, long zoneId, DeployVirtualMachineOptions options);
+
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java
index 0ac02f2..065662b 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java
@@ -28,6 +28,7 @@
 
 import org.jclouds.cloudstack.domain.Account;
 import org.jclouds.cloudstack.domain.Account.State;
+import org.jclouds.cloudstack.domain.FirewallRule;
 import org.jclouds.cloudstack.domain.LoadBalancerRule;
 import org.jclouds.cloudstack.domain.PortForwardingRule;
 import org.jclouds.cloudstack.domain.User;
@@ -46,20 +47,21 @@
 
 /**
  * Configures the cloudstack parsers.
- * 
- * @author Adrian Cole
+ *
+ * @author Adrian Cole, Andrei Savu
  */
 public class CloudStackParserModule extends AbstractModule {
 
-   public static class PortForwardingRuleAdaptor implements JsonSerializer<PortForwardingRule>, JsonDeserializer<PortForwardingRule> {
+   @Singleton
+   public static class PortForwardingRuleAdapter implements JsonSerializer<PortForwardingRule>, JsonDeserializer<PortForwardingRule> {
 
       public JsonElement serialize(PortForwardingRule src, Type typeOfSrc, JsonSerializationContext context) {
          return context.serialize(src);
       }
 
       public PortForwardingRule deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
-            throws JsonParseException {
-         return apply(context.<PortForwardingRuleInternal> deserialize(json, PortForwardingRuleInternal.class));
+         throws JsonParseException {
+         return apply(context.<PortForwardingRuleInternal>deserialize(json, PortForwardingRuleInternal.class));
       }
 
       public PortForwardingRule apply(PortForwardingRuleInternal in) {
@@ -85,10 +87,10 @@
          private long IPAddressId;
          @SerializedName("privateport")
          private int privatePort;
-         private String protocol;
+         private PortForwardingRule.Protocol protocol;
          @SerializedName("publicport")
          public int publicPort;
-         private String state;
+         private PortForwardingRule.State state;
          @SerializedName("virtualmachinedisplayname")
          private String virtualMachineDisplayName;
          @SerializedName("virtualmachineid")
@@ -105,6 +107,52 @@
    }
 
    @Singleton
+   public static class FirewallRuleAdapter implements JsonSerializer<FirewallRule>, JsonDeserializer<FirewallRule> {
+
+      public JsonElement serialize(FirewallRule src, Type typeOfSrc, JsonSerializationContext context) {
+         return context.serialize(src);
+      }
+
+      public FirewallRule deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+         throws JsonParseException {
+         return apply(context.<FirewallRuleInternal>deserialize(json, FirewallRuleInternal.class));
+      }
+
+      public FirewallRule apply(FirewallRuleInternal in) {
+         Set<String> cidrSet;
+         if (in.CIDRs != null) {
+            String[] elements = in.CIDRs.split(",");
+            cidrSet = Sets.newTreeSet(Arrays.asList(elements));
+         } else {
+            cidrSet = Collections.emptySet();
+         }
+         return FirewallRule.builder().id(in.id).CIDRs(cidrSet).startPort(in.startPort).endPort(in.endPort)
+            .icmpCode(in.icmpCode).icmpType(in.icmpType).ipAddress(in.ipAddress).ipAddressId(in.ipAddressId)
+            .protocol(in.protocol).state(in.state).build();
+      }
+
+      static final class FirewallRuleInternal {
+         private long id;
+         @SerializedName("cidrlist")
+         private String CIDRs;
+         @SerializedName("startport")
+         private int startPort;
+         @SerializedName("endport")
+         private int endPort;
+         @SerializedName("icmpcode")
+         private String icmpCode;
+         @SerializedName("icmptype")
+         private String icmpType;
+         @SerializedName("ipaddress")
+         private String ipAddress;
+         @SerializedName("ipaddressid")
+         private long ipAddressId;
+         private FirewallRule.Protocol protocol;
+         private FirewallRule.State state;
+      }
+   }
+
+   @Singleton
    public static class LoadBalancerRuleAdapter implements JsonSerializer<LoadBalancerRule>, JsonDeserializer<LoadBalancerRule> {
 
       public JsonElement serialize(LoadBalancerRule src, Type typeOfSrc, JsonSerializationContext context) {
@@ -112,8 +160,8 @@
       }
 
       public LoadBalancerRule deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
-            throws JsonParseException {
-         return apply(context.<LoadBalancerRuleInternal> deserialize(json, LoadBalancerRuleInternal.class));
+         throws JsonParseException {
+         return apply(context.<LoadBalancerRuleInternal>deserialize(json, LoadBalancerRuleInternal.class));
       }
 
       public LoadBalancerRule apply(LoadBalancerRuleInternal in) {
@@ -157,22 +205,22 @@
       }
 
       public Account deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
-            throws JsonParseException {
-         return apply(context.<AccountInternal> deserialize(json, AccountInternal.class));
+         throws JsonParseException {
+         return apply(context.<AccountInternal>deserialize(json, AccountInternal.class));
       }
 
       public Account apply(AccountInternal in) {
          return Account.builder().id(in.id).type(in.type).domain(in.domain).domainId(in.domainId)
-               .IPsAvailable(nullIfUnlimited(in.IPsAvailable)).IPLimit(nullIfUnlimited(in.IPLimit)).IPs(in.IPs)
-               .cleanupRequired(in.cleanupRequired).name(in.name).receivedBytes(in.receivedBytes)
-               .sentBytes(in.sentBytes).snapshotsAvailable(nullIfUnlimited(in.snapshotsAvailable))
-               .snapshotLimit(nullIfUnlimited(in.snapshotLimit)).snapshots(in.snapshots).state(in.state)
-               .templatesAvailable(nullIfUnlimited(in.templatesAvailable))
-               .templateLimit(nullIfUnlimited(in.templateLimit)).templates(in.templates)
-               .VMsAvailable(nullIfUnlimited(in.VMsAvailable)).VMLimit(nullIfUnlimited(in.VMLimit))
-               .VMsRunning(in.VMsRunning).VMsStopped(in.VMsStopped).VMs(in.VMs)
-               .volumesAvailable(nullIfUnlimited(in.volumesAvailable)).volumeLimit(nullIfUnlimited(in.volumeLimit))
-               .volumes(in.volumes).users(in.users).build();
+            .IPsAvailable(nullIfUnlimited(in.IPsAvailable)).IPLimit(nullIfUnlimited(in.IPLimit)).IPs(in.IPs)
+            .cleanupRequired(in.cleanupRequired).name(in.name).receivedBytes(in.receivedBytes)
+            .sentBytes(in.sentBytes).snapshotsAvailable(nullIfUnlimited(in.snapshotsAvailable))
+            .snapshotLimit(nullIfUnlimited(in.snapshotLimit)).snapshots(in.snapshots).state(in.state)
+            .templatesAvailable(nullIfUnlimited(in.templatesAvailable))
+            .templateLimit(nullIfUnlimited(in.templateLimit)).templates(in.templates)
+            .VMsAvailable(nullIfUnlimited(in.VMsAvailable)).VMLimit(nullIfUnlimited(in.VMLimit))
+            .VMsRunning(in.VMsRunning).VMsStopped(in.VMsStopped).VMs(in.VMs)
+            .volumesAvailable(nullIfUnlimited(in.volumesAvailable)).volumeLimit(nullIfUnlimited(in.volumeLimit))
+            .volumes(in.volumes).users(in.users).build();
       }
 
       static final class AccountInternal {
@@ -237,10 +285,11 @@
    @Override
    protected void configure() {
       bind(new TypeLiteral<Map<Type, Object>>() {
-      }).toInstance(ImmutableMap.<Type, Object> of(
+      }).toInstance(ImmutableMap.<Type, Object>of(
          Account.class, new BreakGenericSetAdapter(),
          LoadBalancerRule.class, new LoadBalancerRuleAdapter(),
-         PortForwardingRule.class, new PortForwardingRuleAdaptor()
+         PortForwardingRule.class, new PortForwardingRuleAdapter(),
+         FirewallRule.class, new FirewallRuleAdapter()
       ));
    }
 
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java
index 5b2bc37..2c28734 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java
@@ -54,6 +54,8 @@
 import org.jclouds.cloudstack.features.GlobalAlertClient;
 import org.jclouds.cloudstack.features.GlobalCapacityAsyncClient;
 import org.jclouds.cloudstack.features.GlobalCapacityClient;
+import org.jclouds.cloudstack.features.GlobalConfigurationAsyncClient;
+import org.jclouds.cloudstack.features.GlobalConfigurationClient;
 import org.jclouds.cloudstack.features.GlobalHostAsyncClient;
 import org.jclouds.cloudstack.features.GlobalHostClient;
 import org.jclouds.cloudstack.features.GlobalOfferingAsyncClient;
@@ -138,6 +140,7 @@
          .put(GuestOSClient.class, GuestOSAsyncClient.class)//
          .put(HypervisorClient.class, HypervisorAsyncClient.class)//
          .put(ConfigurationClient.class, ConfigurationAsyncClient.class)//
+         .put(GlobalConfigurationClient.class, GlobalConfigurationAsyncClient.class)//
          .put(AccountClient.class, AccountAsyncClient.class)//
          .put(DomainAccountClient.class, DomainAccountAsyncClient.class)//
          .put(DomainUserClient.class, DomainUserAsyncClient.class)//
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java
new file mode 100644
index 0000000..edaf2ce
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java
@@ -0,0 +1,248 @@
+/**
+ * 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.cloudstack.domain;
+
+import com.google.gson.annotations.SerializedName;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+
+/**
+ * Represents a CloudStack Cluster.
+ *
+ * @author Richard Downer
+ */
+public class Cluster implements Comparable<Cluster> {
+
+   public enum ManagedState {
+      MANAGED,
+      PREPARE_UNMANAGED,
+      UNMANAGED,
+      PREPARE_UNMANAGED_ERROR,
+      UNRECOGNIZED;
+
+      public static ManagedState fromValue(String value) {
+         try{
+            return valueOf(UPPER_CAMEL.to(UPPER_UNDERSCORE, value));
+         } catch (IllegalArgumentException e) {
+            return UNRECOGNIZED;
+         }
+      }
+
+      @Override
+      public String toString() {
+         return UPPER_UNDERSCORE.to(UPPER_CAMEL, name());
+      }
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private long id;
+      private Host.AllocationState allocationState;
+      private Host.ClusterType clusterType;
+      private String hypervisor;
+      private ManagedState managedState;
+      private String name;
+      private long podId;
+      private String podName;
+      private long zoneId;
+      private String zoneName;
+
+      public Builder id(long id) {
+         this.id = id;
+         return this;
+      }
+
+      public Builder allocationState(Host.AllocationState allocationState) {
+         this.allocationState = allocationState;
+         return this;
+      }
+
+      public Builder clusterType(Host.ClusterType clusterType) {
+         this.clusterType = clusterType;
+         return this;
+      }
+
+      public Builder hypervisor(String hypervisor) {
+         this.hypervisor = hypervisor;
+         return this;
+      }
+
+      public Builder managedState(ManagedState managedState) {
+         this.managedState = managedState;
+         return this;
+      }
+
+      public Builder name(String name) {
+         this.name = name;
+         return this;
+      }
+
+      public Builder podId(long podId) {
+         this.podId = podId;
+         return this;
+      }
+
+      public Builder podName(String podName) {
+         this.podName = podName;
+         return this;
+      }
+
+      public Builder zoneId(long zoneId) {
+         this.zoneId = zoneId;
+         return this;
+      }
+
+      public Builder zoneName(String zoneName) {
+         this.zoneName = zoneName;
+         return this;
+      }
+
+      public Cluster build() {
+         return new Cluster(id, allocationState, clusterType, hypervisor, managedState, name, podId, podName, zoneId, zoneName);
+      }
+   }
+
+   private long id;
+   @SerializedName("allocationstate") private Host.AllocationState allocationState;
+   @SerializedName("clustertype") private Host.ClusterType clusterType;
+   @SerializedName("hypervisortype") private String hypervisor;
+   @SerializedName("managedstate") private ManagedState managedState;
+   private String name;
+   @SerializedName("podid") private long podId;
+   @SerializedName("podname") private String podName;
+   @SerializedName("zoneid") private long zoneId;
+   @SerializedName("zonename") private String zoneName;
+
+   // Just for the serializer
+   Cluster() {}
+
+   public Cluster(long id, Host.AllocationState allocationState, Host.ClusterType clusterType, String hypervisor, ManagedState managedState, String name, long podId, String podName, long zoneId, String zoneName) {
+      this.id = id;
+      this.allocationState = allocationState;
+      this.clusterType = clusterType;
+      this.hypervisor = hypervisor;
+      this.managedState = managedState;
+      this.name = name;
+      this.podId = podId;
+      this.podName = podName;
+      this.zoneId = zoneId;
+      this.zoneName = zoneName;
+   }
+
+   public long getId() {
+      return id;
+   }
+
+   public Host.AllocationState getAllocationState() {
+      return allocationState;
+   }
+
+   public Host.ClusterType getClusterType() {
+      return clusterType;
+   }
+
+   public String getHypervisor() {
+      return hypervisor;
+   }
+
+   public ManagedState getManagedState() {
+      return managedState;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public long getPodId() {
+      return podId;
+   }
+
+   public String getPodName() {
+      return podName;
+   }
+
+   public long getZoneId() {
+      return zoneId;
+   }
+
+   public String getZoneName() {
+      return zoneName;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      Cluster cluster = (Cluster) o;
+
+      if (id != cluster.id) return false;
+      if (podId != cluster.podId) return false;
+      if (zoneId != cluster.zoneId) return false;
+      if (allocationState != cluster.allocationState) return false;
+      if (clusterType != cluster.clusterType) return false;
+      if (hypervisor != null ? !hypervisor.equals(cluster.hypervisor) : cluster.hypervisor != null) return false;
+      if (managedState != cluster.managedState) return false;
+      if (name != null ? !name.equals(cluster.name) : cluster.name != null) return false;
+      if (podName != null ? !podName.equals(cluster.podName) : cluster.podName != null) return false;
+      if (zoneName != null ? !zoneName.equals(cluster.zoneName) : cluster.zoneName != null) return false;
+
+      return true;
+   }
+
+   @Override
+   public int hashCode() {
+      int result = (int) (id ^ (id >>> 32));
+      result = 31 * result + (allocationState != null ? allocationState.hashCode() : 0);
+      result = 31 * result + (clusterType != null ? clusterType.hashCode() : 0);
+      result = 31 * result + (hypervisor != null ? hypervisor.hashCode() : 0);
+      result = 31 * result + (managedState != null ? managedState.hashCode() : 0);
+      result = 31 * result + (name != null ? name.hashCode() : 0);
+      result = 31 * result + (int) (podId ^ (podId >>> 32));
+      result = 31 * result + (podName != null ? podName.hashCode() : 0);
+      result = 31 * result + (int) (zoneId ^ (zoneId >>> 32));
+      result = 31 * result + (zoneName != null ? zoneName.hashCode() : 0);
+      return result;
+   }
+
+   @Override
+   public String toString() {
+      return "Cluster{" +
+         "id=" + id +
+         ", allocationState=" + allocationState +
+         ", clusterType=" + clusterType +
+         ", hypervisor='" + hypervisor + '\'' +
+         ", managedState=" + managedState +
+         ", name='" + name + '\'' +
+         ", podId=" + podId +
+         ", podName='" + podName + '\'' +
+         ", zoneId=" + zoneId +
+         ", zoneName='" + zoneName + '\'' +
+         '}';
+   }
+
+   @Override
+   public int compareTo(Cluster other) {
+      return Long.valueOf(this.id).compareTo(other.id);
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java
new file mode 100644
index 0000000..4756521
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java
@@ -0,0 +1,139 @@
+/**
+ * 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.cloudstack.domain;
+
+/**
+ * Representation of the API configuration entry response
+ *
+ * @author Andrei Savu
+ */
+public class ConfigurationEntry implements Comparable<ConfigurationEntry> {
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+
+      private String category;
+      private String description;
+      private String name;
+      private String value;
+
+      public Builder category(String category) {
+         this.category = category;
+         return this;
+      }
+
+      public Builder description(String description) {
+         this.description = description;
+         return this;
+      }
+
+      public Builder name(String name) {
+         this.name = name;
+         return this;
+      }
+
+      public Builder value(String value) {
+         this.value = value;
+         return this;
+      }
+
+      public ConfigurationEntry build() {
+         return new ConfigurationEntry(category, description, name, value);
+      }
+   }
+
+   // for deserialization
+   ConfigurationEntry() {
+   }
+
+   private String category;
+   private String description;
+   private String name;
+   private String value;
+
+   public ConfigurationEntry(String category, String description, String name, String value) {
+      this.category = category;
+      this.description = description;
+      this.name = name;
+      this.value = value;
+   }
+
+   @Override
+   public int compareTo(ConfigurationEntry arg0) {
+      return name.compareTo(arg0.getName());
+   }
+
+   public String getCategory() {
+      return category;
+   }
+
+   public String getDescription() {
+      return description;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public String getValue() {
+      return value;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      ConfigurationEntry that = (ConfigurationEntry) o;
+
+      if (category != null ? !category.equals(that.category) : that.category != null)
+         return false;
+      if (description != null ? !description.equals(that.description) : that.description != null)
+         return false;
+      if (name != null ? !name.equals(that.name) : that.name != null)
+         return false;
+      if (value != null ? !value.equals(that.value) : that.value != null)
+         return false;
+
+      return true;
+   }
+
+   @Override
+   public int hashCode() {
+      int result = category != null ? category.hashCode() : 0;
+      result = 31 * result + (description != null ? description.hashCode() : 0);
+      result = 31 * result + (name != null ? name.hashCode() : 0);
+      result = 31 * result + (value != null ? value.hashCode() : 0);
+      return result;
+   }
+
+   @Override
+   public String toString() {
+      return "ConfigurationEntry{" +
+         "category='" + category + '\'' +
+         ", description='" + description + '\'' +
+         ", name='" + name + '\'' +
+         ", value='" + value + '\'' +
+         '}';
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java
new file mode 100644
index 0000000..baf5fe6
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java
@@ -0,0 +1,288 @@
+/**
+ * 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.cloudstack.domain;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.annotations.SerializedName;
+import org.omg.PortableInterceptor.ACTIVE;
+
+import java.util.Set;
+
+/**
+ * @author Andrei Savu
+ */
+public class FirewallRule implements Comparable<FirewallRule> {
+
+   public static enum Protocol {
+      TCP,
+      UDP,
+      ICMP,
+      UNKNOWN;
+
+      public static Protocol fromValue(String value) {
+         try {
+            return valueOf(value.toUpperCase());
+         } catch(IllegalArgumentException e) {
+            return UNKNOWN;
+         }
+      }
+
+      @Override
+      public String toString() {
+         return name().toUpperCase();
+      }
+   }
+
+   public static enum State {
+      STAGED,     // Rule been created but has never got through network rule conflict detection.
+                  // Rules in this state can not be sent to network elements.
+      ADD,        // Add means the rule has been created and has gone through network rule conflict detection.
+      ACTIVE,     // Rule has been sent to the network elements and reported to be active.
+      DELETEING,  // Revoke means this rule has been revoked. If this rule has been sent to the
+                  // network elements, the rule will be deleted from database.
+      UNKNOWN;
+
+      public static State fromValue(String value) {
+         try {
+            return valueOf(value.toUpperCase());
+         } catch(IllegalArgumentException e) {
+            return UNKNOWN;
+         }
+      }
+
+      @Override
+      public String toString() {
+         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      }
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private long id;
+      private Set<String> CIDRs;
+
+      private int startPort;
+      private int endPort;
+
+      private String icmpCode;
+      private String icmpType;
+
+      private String ipAddress;
+      private long ipAddressId;
+
+      private Protocol protocol;
+      private State state;
+
+      public Builder id(long id) {
+         this.id = id;
+         return this;
+      }
+
+      public Builder CIDRs(Set<String> CIDRs) {
+         this.CIDRs = ImmutableSet.copyOf(CIDRs);
+         return this;
+      }
+
+      public Builder startPort(int startPort) {
+         this.startPort = startPort;
+         return this;
+      }
+
+      public Builder endPort(int endPort) {
+         this.endPort = endPort;
+         return this;
+      }
+
+      public Builder icmpCode(String icmpCode) {
+         this.icmpCode = icmpCode;
+         return this;
+      }
+
+      public Builder icmpType(String icmpType) {
+         this.icmpType = icmpType;
+         return this;
+      }
+
+      public Builder ipAddress(String ipAddress) {
+         this.ipAddress = ipAddress;
+         return this;
+      }
+
+      public Builder ipAddressId(long ipAddressId) {
+         this.ipAddressId = ipAddressId;
+         return this;
+      }
+
+      public Builder protocol(Protocol protocol) {
+         this.protocol = protocol;
+         return this;
+      }
+
+      public Builder state(State state) {
+         this.state = state;
+         return this;
+      }
+
+      public FirewallRule build() {
+         return new FirewallRule(id, CIDRs, startPort, endPort, icmpCode,
+            icmpType, ipAddress, ipAddressId, protocol, state);
+      }
+   }
+
+   private long id;
+   @SerializedName("cidrlist")
+   private Set<String> CIDRs;
+   @SerializedName("startport")
+   private int startPort;
+   @SerializedName("endport")
+   private int endPort;
+   @SerializedName("icmpcode")
+   private String icmpCode;
+   @SerializedName("icmptype")
+   private String icmpType;
+   @SerializedName("ipaddress")
+   private String ipAddress;
+   @SerializedName("ipaddressid")
+   private long ipAddressId;
+   private Protocol protocol;
+   private State state;
+
+   public FirewallRule(long id, Set<String> CIDRs, int startPort, int endPort,
+         String icmpCode, String icmpType, String ipAddress, long ipAddressId,
+         Protocol protocol, State state) {
+      this.id = id;
+      this.CIDRs = ImmutableSet.copyOf(CIDRs);
+      this.startPort = startPort;
+      this.endPort = endPort;
+      this.icmpCode = icmpCode;
+      this.icmpType = icmpType;
+      this.ipAddress = ipAddress;
+      this.ipAddressId = ipAddressId;
+      this.protocol = protocol;
+      this.state = state;
+   }
+
+   @Override
+   public int compareTo(FirewallRule arg0) {
+      return new Long(id).compareTo(arg0.getId());
+   }
+
+   public long getId() {
+      return id;
+   }
+
+   public Set<String> getCIDRs() {
+      return CIDRs;
+   }
+
+   public int getStartPort() {
+      return startPort;
+   }
+
+   public int getEndPort() {
+      return endPort;
+   }
+
+   public String getIcmpCode() {
+      return icmpCode;
+   }
+
+   public String getIcmpType() {
+      return icmpType;
+   }
+
+   public String getIpAddress() {
+      return ipAddress;
+   }
+
+   public long getIpAddressId() {
+      return ipAddressId;
+   }
+
+   public Protocol getProtocol() {
+      return protocol;
+   }
+
+   public State getState() {
+      return state;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      FirewallRule that = (FirewallRule) o;
+
+      if (endPort != that.endPort) return false;
+      if (id != that.id) return false;
+      if (startPort != that.startPort) return false;
+      if (CIDRs != null ? !CIDRs.equals(that.CIDRs) : that.CIDRs != null)
+         return false;
+      if (icmpCode != null ? !icmpCode.equals(that.icmpCode) : that.icmpCode != null)
+         return false;
+      if (icmpType != null ? !icmpType.equals(that.icmpType) : that.icmpType != null)
+         return false;
+      if (ipAddress != null ? !ipAddress.equals(that.ipAddress) : that.ipAddress != null)
+         return false;
+      if (ipAddressId != that.ipAddressId)
+         return false;
+      if (protocol != null ? !protocol.equals(that.protocol) : that.protocol != null)
+         return false;
+      if (state != null ? !state.equals(that.state) : that.state != null)
+         return false;
+
+      return true;
+   }
+
+   @Override
+   public int hashCode() {
+      int result = (int) (id ^ (id >>> 32));
+      result = 31 * result + (CIDRs != null ? CIDRs.hashCode() : 0);
+      result = 31 * result + startPort;
+      result = 31 * result + endPort;
+      result = 31 * result + (icmpCode != null ? icmpCode.hashCode() : 0);
+      result = 31 * result + (icmpType != null ? icmpType.hashCode() : 0);
+      result = 31 * result + (ipAddress != null ? ipAddress.hashCode() : 0);
+      result = 31 * result + (int) (ipAddressId ^ (ipAddressId >>> 32));
+      result = 31 * result + (protocol != null ? protocol.hashCode() : 0);
+      result = 31 * result + (state != null ? state.hashCode() : 0);
+      return result;
+   }
+
+   @Override
+   public String toString() {
+      return "FirewallRule{" +
+         "id=" + id +
+         ", CIDRs='" + CIDRs + '\'' +
+         ", startPort=" + startPort +
+         ", endPort=" + endPort +
+         ", icmpCode='" + icmpCode + '\'' +
+         ", icmpType='" + icmpType + '\'' +
+         ", ipAddress='" + ipAddress + '\'' +
+         ", ipAddressId='" + ipAddressId + '\'' +
+         ", protocol='" + protocol + '\'' +
+         ", state='" + state + '\'' +
+         '}';
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java
index 73f69ca..823114a 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java
@@ -1,3 +1,21 @@
+/**
+ * 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.cloudstack.domain;
 
 import com.google.gson.annotations.SerializedName;
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java
index e0dada1..67c8ddb 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java
@@ -20,15 +20,58 @@
 
 import java.util.Set;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Splitter;
+import com.google.common.base.CaseFormat;
 import com.google.common.collect.ImmutableSet;
 import com.google.gson.annotations.SerializedName;
 
 /**
- * @author Adrian Cole
+ * @author Adrian Cole, Andrei Savu
  */
 public class PortForwardingRule implements Comparable<PortForwardingRule> {
+
+   public static enum Protocol {
+      TCP,
+      UDP,
+      ICMP,
+      UNKNOWN;
+
+      public static Protocol fromValue(String value) {
+         try {
+            return valueOf(value.toUpperCase());
+         } catch (IllegalArgumentException e) {
+            return UNKNOWN;
+         }
+      }
+
+      @Override
+      public String toString() {
+         return name().toLowerCase();
+      }
+   }
+
+   public static enum State {
+      STAGED,     // Rule been created but has never got through network rule conflict detection.
+                  // Rules in this state can not be sent to network elements.
+      ADD,        // Add means the rule has been created and has gone through network rule conflict detection.
+      ACTIVE,     // Rule has been sent to the network elements and reported to be active.
+      DELETEING,  // Revoke means this rule has been revoked. If this rule has been sent to the
+                  // network elements, the rule will be deleted from database.
+      UNKNOWN;
+
+      public static State fromValue(String value) {
+         try {
+            return valueOf(value.toUpperCase());
+         } catch (IllegalArgumentException e) {
+            return UNKNOWN;
+         }
+      }
+
+      @Override
+      public String toString() {
+         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      }
+   }
+
    public static Builder builder() {
       return new Builder();
    }
@@ -38,9 +81,9 @@
       private String IPAddress;
       private long IPAddressId;
       private int privatePort;
-      private String protocol;
+      private Protocol protocol;
       public int publicPort;
-      private String state;
+      private State state;
       private String virtualMachineDisplayName;
       public long virtualMachineId;
       private String virtualMachineName;
@@ -68,7 +111,7 @@
          return this;
       }
 
-      public Builder protocol(String protocol) {
+      public Builder protocol(Protocol protocol) {
          this.protocol = protocol;
          return this;
       }
@@ -78,7 +121,7 @@
          return this;
       }
 
-      public Builder state(String state) {
+      public Builder state(State state) {
          this.state = state;
          return this;
       }
@@ -115,7 +158,7 @@
 
       public PortForwardingRule build() {
          return new PortForwardingRule(id, IPAddress, IPAddressId, privatePort, protocol, publicPort, state,
-               virtualMachineDisplayName, virtualMachineId, virtualMachineName, CIDRs, privateEndPort, publicEndPort);
+            virtualMachineDisplayName, virtualMachineId, virtualMachineName, CIDRs, privateEndPort, publicEndPort);
       }
    }
 
@@ -126,10 +169,10 @@
    private long IPAddressId;
    @SerializedName("privateport")
    private int privatePort;
-   private String protocol;
+   private Protocol protocol;
    @SerializedName("publicport")
    public int publicPort;
-   private String state;
+   private State state;
    @SerializedName("virtualmachinedisplayname")
    private String virtualMachineDisplayName;
    @SerializedName("virtualmachineid")
@@ -143,8 +186,8 @@
    @SerializedName("publicendport")
    private int publicEndPort;
 
-   public PortForwardingRule(long id, String iPAddress, long iPAddressId, int privatePort, String protocol,
-                             int publicPort, String state, String virtualMachineDisplayName, long virtualMachineId,
+   public PortForwardingRule(long id, String iPAddress, long iPAddressId, int privatePort, Protocol protocol,
+                             int publicPort, State state, String virtualMachineDisplayName, long virtualMachineId,
                              String virtualMachineName, Set<String> CIDRs, int privateEndPort, int publicEndPort) {
       this.id = id;
       this.IPAddress = iPAddress;
@@ -197,7 +240,7 @@
    /**
     * @return the protocol of the port forwarding rule
     */
-   public String getProtocol() {
+   public Protocol getProtocol() {
       return protocol;
    }
 
@@ -211,7 +254,7 @@
    /**
     * @return the state of the rule
     */
-   public String getState() {
+   public State getState() {
       return state;
    }
 
@@ -324,20 +367,20 @@
    @Override
    public String toString() {
       return "PortForwardingRule{" +
-            "id=" + id +
-            ", IPAddress='" + IPAddress + '\'' +
-            ", IPAddressId=" + IPAddressId +
-            ", privatePort=" + privatePort +
-            ", protocol='" + protocol + '\'' +
-            ", publicPort=" + publicPort +
-            ", state='" + state + '\'' +
-            ", virtualMachineDisplayName='" + virtualMachineDisplayName + '\'' +
-            ", virtualMachineId=" + virtualMachineId +
-            ", virtualMachineName='" + virtualMachineName + '\'' +
-            ", CIDRs=" + getCIDRs() +
-            ", privateEndPort=" + privateEndPort +
-            ", publicEndPort=" + publicEndPort +
-            '}';
+         "id=" + id +
+         ", IPAddress='" + IPAddress + '\'' +
+         ", IPAddressId=" + IPAddressId +
+         ", privatePort=" + privatePort +
+         ", protocol='" + protocol + '\'' +
+         ", publicPort=" + publicPort +
+         ", state='" + state + '\'' +
+         ", virtualMachineDisplayName='" + virtualMachineDisplayName + '\'' +
+         ", virtualMachineId=" + virtualMachineId +
+         ", virtualMachineName='" + virtualMachineName + '\'' +
+         ", CIDRs=" + getCIDRs() +
+         ", privateEndPort=" + privateEndPort +
+         ", publicEndPort=" + publicEndPort +
+         '}';
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/FirewallAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/FirewallAsyncClient.java
index 85b63b0..5c9ef48 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/FirewallAsyncClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/FirewallAsyncClient.java
@@ -26,15 +26,20 @@
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.FirewallRule;
 import org.jclouds.cloudstack.domain.PortForwardingRule;
 import org.jclouds.cloudstack.filters.QuerySigner;
+import org.jclouds.cloudstack.options.CreateFirewallRuleOptions;
+import org.jclouds.cloudstack.options.ListFirewallRulesOptions;
 import org.jclouds.cloudstack.options.ListPortForwardingRulesOptions;
 import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.OnlyElement;
 import org.jclouds.rest.annotations.QueryParams;
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.SelectJson;
 import org.jclouds.rest.annotations.Unwrap;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
 
 import com.google.common.util.concurrent.ListenableFuture;
@@ -42,16 +47,55 @@
 /**
  * Provides asynchronous access to cloudstack via their REST API.
  * <p/>
- * 
+ *
+ * @author Adrian Cole
  * @see FirewallClient
  * @see <a href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html" />
- * @author Adrian Cole
  */
 @RequestFilters(QuerySigner.class)
 @QueryParams(keys = "response", values = "json")
 public interface FirewallAsyncClient {
 
    /**
+    * @see FirewallClient#listFirewallRules
+    */
+   @GET
+   @QueryParams(keys = "command", values = "listFirewallRules")
+   @SelectJson("firewallrule")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   ListenableFuture<Set<FirewallRule>> listFirewallRules(ListFirewallRulesOptions... options);
+
+   /**
+    * @see FirewallClient#getFirewallRule
+    */
+   @GET
+   @QueryParams(keys = "command", values = "listFirewallRules")
+   @SelectJson("firewallrule")
+   @OnlyElement
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<FirewallRule> getFirewallRule(@QueryParam("id") long id);
+
+   /**
+    * @see FirewallClient#createFirewallRuleForIpAndProtocol
+    */
+   @GET
+   @QueryParams(keys = "command", values = "createFirewallRule")
+   @Unwrap
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<AsyncCreateResponse> createFirewallRuleForIpAndProtocol(@QueryParam("ipaddressid") long ipAddressId,
+         @QueryParam("protocol") FirewallRule.Protocol protocol, CreateFirewallRuleOptions... options);
+
+   /**
+    * @see FirewallClient#deleteFirewallRule
+    */
+   @GET
+   @QueryParams(keys = "command", values = "deleteFirewallRule")
+   @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
+   ListenableFuture<Void> deleteFirewallRule(@QueryParam("id") long id);
+
+   /**
     * @see FirewallClient#listPortForwardingRules
     */
    @GET
@@ -62,6 +106,17 @@
    ListenableFuture<Set<PortForwardingRule>> listPortForwardingRules(ListPortForwardingRulesOptions... options);
 
    /**
+    * @see FirewallClient#getPortForwardingRule
+    */
+   @GET
+   @QueryParams(keys = "command", values = "listPortForwardingRules")
+   @SelectJson("portforwardingrule")
+   @OnlyElement
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<PortForwardingRule> getPortForwardingRule(@QueryParam("id") long id);
+
+   /**
     * @see FirewallClient#createPortForwardingRuleForVirtualMachine
     */
    @GET
@@ -69,9 +124,9 @@
    @Unwrap
    @Consumes(MediaType.APPLICATION_JSON)
    ListenableFuture<AsyncCreateResponse> createPortForwardingRuleForVirtualMachine(
-         @QueryParam("virtualmachineid") long virtualMachineId, @QueryParam("ipaddressid") long IPAddressId,
-         @QueryParam("protocol") String protocol, @QueryParam("privateport") int privatePort,
-         @QueryParam("publicport") int publicPort);
+      @QueryParam("ipaddressid") long ipAddressId, @QueryParam("protocol") PortForwardingRule.Protocol protocol,
+      @QueryParam("publicport") int publicPort, @QueryParam("virtualmachineid") long virtualMachineId,
+      @QueryParam("privateport") int privatePort);
 
    /**
     * @see FirewallClient#deletePortForwardingRule
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/FirewallClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/FirewallClient.java
index 3480016..ae02685 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/FirewallClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/FirewallClient.java
@@ -22,7 +22,10 @@
 import java.util.concurrent.TimeUnit;
 
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.FirewallRule;
 import org.jclouds.cloudstack.domain.PortForwardingRule;
+import org.jclouds.cloudstack.options.CreateFirewallRuleOptions;
+import org.jclouds.cloudstack.options.ListFirewallRulesOptions;
 import org.jclouds.cloudstack.options.ListPortForwardingRulesOptions;
 import org.jclouds.concurrent.Timeout;
 
@@ -36,6 +39,49 @@
  */
 @Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
 public interface FirewallClient {
+
+   /**
+    * List the firewall rules
+    *
+    * @param options
+    *          if present, how to constrain the list.
+    * @return
+    *          set of firewall rules or empty set if no rules are found
+    */
+   Set<FirewallRule> listFirewallRules(ListFirewallRulesOptions... options);
+
+   /**
+    * Get a firewall rule by ID
+    *
+    * @param id
+    *          the ID of the firewall rule
+    * @return
+    *          firewall rule instance or null
+    */
+   FirewallRule getFirewallRule(long id);
+
+   /**
+    * Create new firewall rule for a specific IP address
+    *
+    * @param ipAddressId
+    *          the IP address id of the port forwarding rule
+    * @param protocol
+    *          the protocol for the firewall rule. Valid values are TCP/UDP/ICMP
+    * @param options
+    *          optional arguments for firewall rule creation
+    * @return
+    */
+   AsyncCreateResponse createFirewallRuleForIpAndProtocol(long ipAddressId,
+         FirewallRule.Protocol protocol, CreateFirewallRuleOptions... options);
+
+   /**
+    * Deletes a firewall rule
+    *
+    * @param id
+    *       the ID of the firewall rule
+    */
+   Void deleteFirewallRule(long id);
+
    /**
     * List the port forwarding rules
     * 
@@ -47,23 +93,32 @@
    Set<PortForwardingRule> listPortForwardingRules(ListPortForwardingRulesOptions... options);
 
    /**
+    * Get a port forwarding rule by ID
+    *
+    * @param id
+    *       port forwarding rule ID
+    * @return
+    *       rule instance or null
+    */
+   PortForwardingRule getPortForwardingRule(long id);
+
+   /**
     * Creates an port forwarding rule
     * 
-    * @param virtualMachineId
-    *           the ID of the virtual machine for the port forwarding rule
-    * @param IPAddressId
-    *           the public IP address id of the forwarding rule, already
-    *           associated via associatePort
+    *
+    * @param ipAddressId
     * @param protocol
     *           the protocol for the rule. Valid values are TCP or UDP.
-    * @param privatePort
-    *           the private port of the port forwarding rule
     * @param publicPort
     *           the public port of the port forwarding rule
+    * @param virtualMachineId
+    *           the ID of the virtual machine for the port forwarding rule
+    * @param privatePort
+    *           the private port of the port forwarding rule
     * @return response used to track creation
     */
-   AsyncCreateResponse createPortForwardingRuleForVirtualMachine(long virtualMachineId, long IPAddressId,
-         String protocol, int privatePort, int publicPort);
+   AsyncCreateResponse createPortForwardingRuleForVirtualMachine(long ipAddressId,
+      PortForwardingRule.Protocol protocol, int publicPort, long virtualMachineId, int privatePort);
 
    /**
     * Deletes an port forwarding rule
@@ -71,5 +126,5 @@
     * @param id
     *           the id of the forwarding rule
     */
-   void deletePortForwardingRule(long id);
+   Void deletePortForwardingRule(long id);
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalConfigurationAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalConfigurationAsyncClient.java
new file mode 100644
index 0000000..21212e3
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalConfigurationAsyncClient.java
@@ -0,0 +1,72 @@
+/**
+ * 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.cloudstack.features;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.jclouds.cloudstack.domain.ConfigurationEntry;
+import org.jclouds.cloudstack.filters.QuerySigner;
+import org.jclouds.cloudstack.options.ListConfigurationEntriesOptions;
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.OnlyElement;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.util.Set;
+
+/**
+ * Provides asynchronous access to CloudStack Configuration features available to Global
+ * Admin users.
+ *
+ * @author Andrei Savu
+ * @see <a href=
+ *      "http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_Global_Admin.html"
+ *      />
+ */
+@RequestFilters(QuerySigner.class)
+@QueryParams(keys = "response", values = "json")
+public interface GlobalConfigurationAsyncClient extends ConfigurationAsyncClient {
+
+   /**
+    * @see GlobalConfigurationClient#listConfigurationEntries
+    */
+   @GET
+   @QueryParams(keys = "command", values = "listConfigurations")
+   @SelectJson("configuration")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   ListenableFuture<Set<ConfigurationEntry>> listConfigurationEntries(ListConfigurationEntriesOptions... options);
+
+   /**
+    * @see GlobalConfigurationClient#updateConfigurationEntry
+    */
+   @GET
+   @QueryParams(keys = "command", values = "updateConfiguration")
+   @SelectJson("configuration")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<ConfigurationEntry> updateConfigurationEntry(
+      @QueryParam("name") String name, @QueryParam("value") String value);
+}
\ No newline at end of file
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalConfigurationClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalConfigurationClient.java
new file mode 100644
index 0000000..1b03c43
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalConfigurationClient.java
@@ -0,0 +1,61 @@
+/**
+ * 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.cloudstack.features;
+
+import org.jclouds.cloudstack.domain.ConfigurationEntry;
+import org.jclouds.cloudstack.options.ListConfigurationEntriesOptions;
+import org.jclouds.concurrent.Timeout;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provides synchronous access to CloudStack Configuration features available to Global
+ * Admin users.
+ * 
+ * @author Andrei Savu
+ * @see <a href=
+ *      "http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_Global_Admin.html"
+ *      />
+ */
+@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
+public interface GlobalConfigurationClient extends ConfigurationClient {
+
+   /**
+    * List all configuration entries
+    *
+    * @param options
+    *          result set filtering options
+    * @return
+    *          a set of entries or empty
+    */
+   Set<ConfigurationEntry> listConfigurationEntries(ListConfigurationEntriesOptions... options);
+
+   /**
+    * Update a configuration entry
+    *
+    * @param name
+    *          the name of the configuration
+    * @param value
+    *          the value of the configuration
+    * @return
+    *          the updated configuration value
+    */
+   ConfigurationEntry updateConfigurationEntry(String name, String value);
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostAsyncClient.java
index 2a91b08..a6fc4d0 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostAsyncClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostAsyncClient.java
@@ -19,8 +19,10 @@
 package org.jclouds.cloudstack.features;
 
 import com.google.common.util.concurrent.ListenableFuture;
+import org.jclouds.cloudstack.domain.Cluster;
 import org.jclouds.cloudstack.domain.Host;
 import org.jclouds.cloudstack.filters.QuerySigner;
+import org.jclouds.cloudstack.options.ListClustersOptions;
 import org.jclouds.cloudstack.options.ListHostsOptions;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.QueryParams;
@@ -54,4 +56,14 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
    ListenableFuture<Set<Host>> listHosts(ListHostsOptions... options);
+
+   /**
+    * @see GlobalHostClient#listClusters
+    */
+   @GET
+   @QueryParams(keys = "command", values = "listClusters")
+   @SelectJson("cluster")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   ListenableFuture<Set<Cluster>> listClusters(ListClustersOptions... options);
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostClient.java
index 6103fe4..414ce8b 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/GlobalHostClient.java
@@ -18,7 +18,9 @@
  */
 package org.jclouds.cloudstack.features;
 
+import org.jclouds.cloudstack.domain.Cluster;
 import org.jclouds.cloudstack.domain.Host;
+import org.jclouds.cloudstack.options.ListClustersOptions;
 import org.jclouds.cloudstack.options.ListHostsOptions;
 import org.jclouds.concurrent.Timeout;
 
@@ -46,4 +48,11 @@
     */
    Set<Host> listHosts(ListHostsOptions... options);
 
+   /**
+    * Lists clusters
+    *
+    * @param options if present, how to constrain the list
+    * @return clusters matching query, or empty set if no clusters match
+    */
+   Set<Cluster> listClusters(ListClustersOptions... options);
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClient.java
index db05a7e..da9b821 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClient.java
@@ -29,7 +29,9 @@
 import org.jclouds.cloudstack.domain.LoadBalancerRule.Algorithm;
 import org.jclouds.cloudstack.domain.VirtualMachine;
 import org.jclouds.cloudstack.filters.QuerySigner;
+import org.jclouds.cloudstack.options.CreateLoadBalancerRuleOptions;
 import org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions;
+import org.jclouds.cloudstack.options.UpdateLoadBalancerRuleOptions;
 import org.jclouds.functions.JoinOnComma;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.OnlyElement;
@@ -76,7 +78,7 @@
    ListenableFuture<LoadBalancerRule> getLoadBalancerRule(@QueryParam("id") long id);
 
    /**
-    * @see LoadBalancerClient#createLoadBalancerRuleForPublicIp
+    * @see LoadBalancerClient#createLoadBalancerRuleForPublicIP
     */
    @GET
    @QueryParams(keys = "command", values = "createLoadBalancerRule")
@@ -84,7 +86,19 @@
    @Consumes(MediaType.APPLICATION_JSON)
    ListenableFuture<Long> createLoadBalancerRuleForPublicIP(@QueryParam("publicipid") long publicIPId,
          @QueryParam("algorithm") Algorithm algorithm, @QueryParam("name") String name,
-         @QueryParam("privateport") int privatePort, @QueryParam("publicport") int publicPort);
+         @QueryParam("privateport") int privatePort, @QueryParam("publicport") int publicPort,
+         CreateLoadBalancerRuleOptions... options);
+
+   /**
+    * @see LoadBalancerClient#updateLoadBalancerRule
+    */
+   @GET
+   @QueryParams(keys = "command", values ="updateLoadBalancerRule")
+   @SelectJson("loadbalancerrule")
+   @OnlyElement
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<LoadBalancerRule> updateLoadBalancerRule(@QueryParam("id") long id, UpdateLoadBalancerRuleOptions... options);
 
    /**
     * @see LoadBalancerClient#deleteLoadBalancerRule
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/LoadBalancerClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/LoadBalancerClient.java
index 8e6da5e..ab20d2e 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/LoadBalancerClient.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/LoadBalancerClient.java
@@ -24,7 +24,9 @@
 import org.jclouds.cloudstack.domain.LoadBalancerRule;
 import org.jclouds.cloudstack.domain.LoadBalancerRule.Algorithm;
 import org.jclouds.cloudstack.domain.VirtualMachine;
+import org.jclouds.cloudstack.options.CreateLoadBalancerRuleOptions;
 import org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions;
+import org.jclouds.cloudstack.options.UpdateLoadBalancerRuleOptions;
 import org.jclouds.concurrent.Timeout;
 
 /**
@@ -72,10 +74,22 @@
     * @param publicPort
     *           public ip address id from where the network traffic will be load
     *           balanced from
+    * @param options optional call arguments
     * @return newly created rule
     */
    Long createLoadBalancerRuleForPublicIP(long publicIPId, Algorithm algorithm, String name,
-         int privatePort, int publicPort);
+         int privatePort, int publicPort, CreateLoadBalancerRuleOptions... options);
+
+   /**
+    * Update a load balancer rule.
+    *
+    * @param id
+    *       rule id
+    * @param options
+    *       optional arguments
+    * @return updated rule
+    */
+   LoadBalancerRule updateLoadBalancerRule(long id, UpdateLoadBalancerRuleOptions... options);
 
    /**
     * 
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ZoneIdToZone.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ZoneIdToZone.java
new file mode 100644
index 0000000..8ee174f
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ZoneIdToZone.java
@@ -0,0 +1,50 @@
+/**
+ * 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.cloudstack.functions;
+
+import com.google.common.cache.CacheLoader;
+import com.google.inject.Inject;
+import org.jclouds.cloudstack.CloudStackClient;
+import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.features.ZoneClient;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Defines a cache that allows a zone to be looked up by its ID.
+ *
+ * @author Richard Downer
+ */
+public class ZoneIdToZone extends CacheLoader<Long, Zone> {
+
+   private final ZoneClient zoneClient;
+
+   @Inject
+   public ZoneIdToZone(CloudStackClient client) {
+      checkNotNull(client, "client");
+      this.zoneClient = client.getZoneClient();
+   }
+
+   @Override
+   public Zone load(Long zoneId) throws Exception {
+      checkNotNull(zoneId, "zoneId");
+      return zoneClient.getZone(zoneId);
+   }
+
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/internal/CloudStackContextImpl.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/internal/CloudStackContextImpl.java
index d033e5e..34b8e70 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/internal/CloudStackContextImpl.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/internal/CloudStackContextImpl.java
@@ -1,3 +1,21 @@
+/**
+ * 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.cloudstack.internal;
 
 import java.util.Map;
@@ -24,19 +42,27 @@
 @Singleton
 public class CloudStackContextImpl extends ComputeServiceContextImpl<CloudStackClient, CloudStackAsyncClient> implements
       CloudStackContext {
+   private final RestContext<CloudStackClient,CloudStackAsyncClient> providerSpecificContext;
    private final RestContext<CloudStackDomainClient, CloudStackDomainAsyncClient> domainContext;
    private final RestContext<CloudStackGlobalClient, CloudStackGlobalAsyncClient> globalContext;
 
    @Inject
    public CloudStackContextImpl(ComputeService computeService, Map<String, Credentials> credentialStore, Utils utils,
-         @SuppressWarnings("rawtypes") RestContext providerSpecificContext,
+         RestContext<CloudStackClient,CloudStackAsyncClient> providerSpecificContext,
          RestContext<CloudStackDomainClient, CloudStackDomainAsyncClient> domainContext,
          RestContext<CloudStackGlobalClient, CloudStackGlobalAsyncClient> globalContext) {
       super(computeService, credentialStore, utils, providerSpecificContext);
+      this.providerSpecificContext=providerSpecificContext;
       this.domainContext = domainContext;
       this.globalContext = globalContext;
    }
-
+   
+   @SuppressWarnings("unchecked")
+   @Override
+   public RestContext<CloudStackClient,CloudStackAsyncClient> getProviderSpecificContext() {
+      return providerSpecificContext;
+   }
+   
    @Override
    public RestContext<CloudStackDomainClient, CloudStackDomainAsyncClient> getDomainContext() {
       return domainContext;
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateFirewallRuleOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateFirewallRuleOptions.java
new file mode 100644
index 0000000..00b37bd
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateFirewallRuleOptions.java
@@ -0,0 +1,126 @@
+/**
+ * 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.cloudstack.options;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+import java.util.Set;
+
+/**
+ * Options used to control how a firewall rule is created
+ * 
+ * @see <a href=
+ *      "http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/createFirewallRule.html"
+ *      />
+ * @author Andrei Savu
+ */
+public class CreateFirewallRuleOptions extends BaseHttpRequestOptions {
+
+   public static final CreateFirewallRuleOptions NONE = new CreateFirewallRuleOptions();
+
+   /**
+    * @param CIDRs
+    *       the list of CIDRs to forward traffic from
+    */
+   public CreateFirewallRuleOptions CIDRs(Set<String> CIDRs) {
+      this.queryParameters.replaceValues("cidrlist", ImmutableSet.of(Joiner.on(",").join(CIDRs)));
+      return this;
+   }
+
+   /**
+    * @param startPort
+    *       the starting port of firewall rule
+    */
+   public CreateFirewallRuleOptions startPort(int startPort) {
+      this.queryParameters.replaceValues("startport", ImmutableSet.of(startPort + ""));
+      return this;
+   }
+
+   /**
+    * @param endPort
+    *       the ending port of firewall rule
+    */
+   public CreateFirewallRuleOptions endPort(int endPort) {
+      this.queryParameters.replaceValues("endport", ImmutableSet.of(endPort + ""));
+      return this;
+   }
+
+   /**
+    * @param icmpCode
+    *       error code for this icmp message
+    */
+   public CreateFirewallRuleOptions icmpCode(String icmpCode) {
+      this.queryParameters.replaceValues("icmpcode", ImmutableSet.of(icmpCode));
+      return this;
+   }
+
+   /**
+    * @param icmpType
+    *       type of the icmp message being sent
+    */
+   public CreateFirewallRuleOptions icmpType(String icmpType) {
+      this.queryParameters.replaceValues("icmptype", ImmutableSet.of(icmpType));
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see CreateFirewallRuleOptions#CIDRs
+       */
+      public static CreateFirewallRuleOptions CIDRs(Set<String> CIDRs) {
+         CreateFirewallRuleOptions options = new CreateFirewallRuleOptions();
+         return options.CIDRs(CIDRs);
+      }
+
+      /**
+       * @see CreateFirewallRuleOptions#startPort
+       */
+      public static CreateFirewallRuleOptions startPort(int startPort) {
+         CreateFirewallRuleOptions options = new CreateFirewallRuleOptions();
+         return options.startPort(startPort);
+      }
+
+      /**
+       * @see CreateFirewallRuleOptions#endPort
+       */
+      public static CreateFirewallRuleOptions endPort(int endPort) {
+         CreateFirewallRuleOptions options = new CreateFirewallRuleOptions();
+         return options.endPort(endPort);
+      }
+
+      /**
+       * @see CreateFirewallRuleOptions#icmpCode
+       */
+      public static CreateFirewallRuleOptions icmpCode(String icmpCode) {
+         CreateFirewallRuleOptions options = new CreateFirewallRuleOptions();
+         return options.icmpCode(icmpCode);
+      }
+
+      /**
+       * @see CreateFirewallRuleOptions#icmpType
+       */
+      public static CreateFirewallRuleOptions icmpType(String icmpType) {
+         CreateFirewallRuleOptions options = new CreateFirewallRuleOptions();
+         return options.icmpType(icmpType);
+      }
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateLoadBalancerRuleOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateLoadBalancerRuleOptions.java
new file mode 100644
index 0000000..c1a0049
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateLoadBalancerRuleOptions.java
@@ -0,0 +1,138 @@
+/**
+ * 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.cloudstack.options;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Set;
+
+/**
+ * Options used to control what load balancer rules are returned
+ *
+ * @author Adrian Cole, Andrei Savu
+ * @see <a href=
+ *      "http://download.cloud.com/releases/2.2.0/api_2.2.12/user/createLoadBalancerRule.html"
+ *      />
+ */
+public class CreateLoadBalancerRuleOptions extends AccountInDomainOptions {
+
+   public static final CreateLoadBalancerRuleOptions NONE = new CreateLoadBalancerRuleOptions();
+
+   /**
+    * @param allowedSourceCIRDs the cidr list to forward traffic from
+    */
+   public CreateLoadBalancerRuleOptions allowedSourceCIDRs(Set<String> allowedSourceCIRDs) {
+      this.queryParameters.replaceValues("cidrlist",
+         ImmutableSet.of(Joiner.on(",").join(allowedSourceCIRDs)));
+      return this;
+   }
+
+   /**
+    * @param description the description of the load balancer rule
+    */
+   public CreateLoadBalancerRuleOptions description(String description) {
+      this.queryParameters.replaceValues("description", ImmutableSet.of(description));
+      return this;
+   }
+
+   /**
+    * @param openFirewall if true, firewall rule for source/end pubic port is automatically
+    *    created; if false - firewall rule has to be created explicitly. Has value true by default
+    */
+   public CreateLoadBalancerRuleOptions openFirewall(boolean openFirewall) {
+      this.queryParameters.replaceValues("openfirewall", ImmutableSet.of(openFirewall + ""));
+      return this;
+   }
+
+   /**
+    * @param zoneId the availability zone ID
+    */
+   public CreateLoadBalancerRuleOptions zoneId(long zoneId) {
+      this.queryParameters.replaceValues("zoneid", ImmutableSet.of(zoneId + ""));
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see CreateLoadBalancerRuleOptions#allowedSourceCIDRs
+       */
+      public static CreateLoadBalancerRuleOptions allowedSourceCIDRs(Set<String> allowedSourceCIDRs) {
+         CreateLoadBalancerRuleOptions options = new CreateLoadBalancerRuleOptions();
+         return options.allowedSourceCIDRs(allowedSourceCIDRs);
+      }
+
+      /**
+       * @see CreateLoadBalancerRuleOptions#description
+       */
+      public static CreateLoadBalancerRuleOptions description(String description) {
+         CreateLoadBalancerRuleOptions options = new CreateLoadBalancerRuleOptions();
+         return options.description(description);
+      }
+
+      /**
+       * @see CreateLoadBalancerRuleOptions#openFirewall
+       */
+      public static CreateLoadBalancerRuleOptions openFirewall(boolean openFirewall) {
+         CreateLoadBalancerRuleOptions options = new CreateLoadBalancerRuleOptions();
+         return options.openFirewall(openFirewall);
+      }
+
+      /**
+       * @see CreateLoadBalancerRuleOptions#zoneId
+       */
+      public static CreateLoadBalancerRuleOptions zoneId(long zoneId) {
+         CreateLoadBalancerRuleOptions options = new CreateLoadBalancerRuleOptions();
+         return options.zoneId(zoneId);
+      }
+
+      /**
+       * @see CreateLoadBalancerRuleOptions#accountInDomain
+       */
+      public static CreateLoadBalancerRuleOptions accountInDomain(String account, long domain) {
+         CreateLoadBalancerRuleOptions options = new CreateLoadBalancerRuleOptions();
+         return options.accountInDomain(account, domain);
+      }
+
+      /**
+       * @see CreateLoadBalancerRuleOptions#domainId
+       */
+      public static CreateLoadBalancerRuleOptions domainId(long id) {
+         CreateLoadBalancerRuleOptions options = new CreateLoadBalancerRuleOptions();
+         return options.domainId(id);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public CreateLoadBalancerRuleOptions accountInDomain(String account, long domain) {
+      return CreateLoadBalancerRuleOptions.class.cast(super.accountInDomain(account, domain));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public CreateLoadBalancerRuleOptions domainId(long domainId) {
+      return CreateLoadBalancerRuleOptions.class.cast(super.domainId(domainId));
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateSnapshotOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateSnapshotOptions.java
index 40ddc09..7a1ce24 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateSnapshotOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/CreateSnapshotOptions.java
@@ -1,4 +1,22 @@
 /**
+ * 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.
+ */
+/**
    * Licensed to jclouds, Inc. (jclouds) under one or more
    * contributor license agreements.  See the NOTICE file
    * distributed with this work for additional information
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeleteISOOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeleteISOOptions.java
index d0752d8..21061bb 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeleteISOOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeleteISOOptions.java
@@ -1,4 +1,22 @@
 /**
+ * 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.
+ */
+/**
    * Licensed to jclouds, Inc. (jclouds) under one or more
    * contributor license agreements.  See the NOTICE file
    * distributed with this work for additional information
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ExtractISOOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ExtractISOOptions.java
index 9d70c6d..a0b8de2 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ExtractISOOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ExtractISOOptions.java
@@ -1,4 +1,22 @@
 /**
+ * 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.
+ */
+/**
    * Licensed to jclouds, Inc. (jclouds) under one or more
    * contributor license agreements.  See the NOTICE file
    * distributed with this work for additional information
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListClustersOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListClustersOptions.java
new file mode 100644
index 0000000..667b403
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListClustersOptions.java
@@ -0,0 +1,122 @@
+/**
+ * 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.cloudstack.options;
+
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudstack.domain.Cluster;
+import org.jclouds.cloudstack.domain.Host;
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options used to control what cluster information is returned
+ *
+ * @author Richard Downer
+ * @see <a
+ *      href="http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/listClusters.html"
+ *      />
+ */
+public class ListClustersOptions extends BaseHttpRequestOptions {
+
+   public static final ListHostsOptions NONE = new ListHostsOptions();
+
+   public ListClustersOptions allocationState(Host.AllocationState allocationState) {
+      this.queryParameters.replaceValues("allocationstate", ImmutableSet.of(allocationState.toString()));
+      return this;
+   }
+
+   public ListClustersOptions clusterType(Host.ClusterType clusterType) {
+      this.queryParameters.replaceValues("clustertype", ImmutableSet.of(clusterType.toString()));
+      return this;
+   }
+
+   public ListClustersOptions hypervisor(String hypervisor) {
+      this.queryParameters.replaceValues("hypervisor", ImmutableSet.of(hypervisor));
+      return this;
+   }
+
+   public ListClustersOptions id(long id) {
+      this.queryParameters.replaceValues("id", ImmutableSet.of(id + ""));
+      return this;
+   }
+
+   public ListClustersOptions keyword(String keyword) {
+      this.queryParameters.replaceValues("keyword", ImmutableSet.of(keyword));
+      return this;
+   }
+
+   public ListClustersOptions managedState(Cluster.ManagedState managedState) {
+      this.queryParameters.replaceValues("managedstate", ImmutableSet.of(managedState.toString()));
+      return this;
+   }
+
+   public ListClustersOptions name(String name) {
+      this.queryParameters.replaceValues("name", ImmutableSet.of(name));
+      return this;
+   }
+
+   public ListClustersOptions podId(long podId) {
+      this.queryParameters.replaceValues("podid", ImmutableSet.of(podId + ""));
+      return this;
+   }
+
+   public ListClustersOptions zoneId(long zoneId) {
+      this.queryParameters.replaceValues("zoneid", ImmutableSet.of(zoneId + ""));
+      return this;
+   }
+
+   public static class Builder {
+
+      public static ListClustersOptions allocationState(Host.AllocationState allocationState) {
+         return new ListClustersOptions().allocationState(allocationState);
+      }
+
+      public static ListClustersOptions clusterType(Host.ClusterType clusterType) {
+         return new ListClustersOptions().clusterType(clusterType);
+      }
+
+      public static ListClustersOptions hypervisor(String hypervisor) {
+         return new ListClustersOptions().hypervisor(hypervisor);
+      }
+
+      public static ListClustersOptions id(long id) {
+         return new ListClustersOptions().id(id);
+      }
+
+      public static ListClustersOptions keyword(String keyword) {
+         return new ListClustersOptions().keyword(keyword);
+      }
+
+      public static ListClustersOptions managedState(Cluster.ManagedState managedState) {
+         return new ListClustersOptions().managedState(managedState);
+      }
+
+      public static ListClustersOptions name(String name) {
+         return new ListClustersOptions().name(name);
+      }
+
+      public static ListClustersOptions podId(long podId) {
+         return new ListClustersOptions().podId(podId);
+      }
+
+      public static ListClustersOptions zoneId(long zoneId) {
+         return new ListClustersOptions().zoneId(zoneId);
+      }
+
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListConfigurationEntriesOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListConfigurationEntriesOptions.java
new file mode 100644
index 0000000..1f585ae
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListConfigurationEntriesOptions.java
@@ -0,0 +1,115 @@
+/**
+ * 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.cloudstack.options;
+
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options used to control what configuration entries are returned
+ * 
+ * @see <a href=
+ *      "http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/listConfigurations.html"
+ *      />
+ * @author Andrei Savu
+ */
+public class ListConfigurationEntriesOptions extends BaseHttpRequestOptions {
+
+   public static final ListConfigurationEntriesOptions NONE = new ListConfigurationEntriesOptions();
+
+   /**
+    * @param category
+    *    list by category name
+    */
+   public ListConfigurationEntriesOptions category(String category) {
+      this.queryParameters.replaceValues("category", ImmutableSet.of(category));
+      return this;
+   }
+
+   /**
+    * @param keyword
+    *    list by keyword
+    */
+   public ListConfigurationEntriesOptions keyword(String keyword) {
+      this.queryParameters.replaceValues("keyword", ImmutableSet.of(keyword));
+      return this;
+   }
+
+   /**
+    * @param name
+    *    list by entry name
+    */
+   public ListConfigurationEntriesOptions name(String name) {
+      this.queryParameters.replaceValues("name", ImmutableSet.of(name));
+      return this;
+   }
+
+   public ListConfigurationEntriesOptions page(long page) {
+      this.queryParameters.replaceValues("page", ImmutableSet.of(page + ""));
+      return this;
+   }
+
+   public ListConfigurationEntriesOptions pageSize(long pageSize) {
+      this.queryParameters.replaceValues("pagesize", ImmutableSet.of(pageSize + ""));
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see ListConfigurationEntriesOptions#category
+       */
+      public static ListConfigurationEntriesOptions category(String category) {
+         ListConfigurationEntriesOptions options = new ListConfigurationEntriesOptions();
+         return options.category(category);
+      }
+
+      /**
+       * @see ListConfigurationEntriesOptions#keyword
+       */
+      public static ListConfigurationEntriesOptions keyword(String keyword) {
+         ListConfigurationEntriesOptions options = new ListConfigurationEntriesOptions();
+         return options.keyword(keyword);
+      }
+
+      /**
+       * @see ListConfigurationEntriesOptions#name
+       */
+      public static ListConfigurationEntriesOptions name(String name) {
+         ListConfigurationEntriesOptions options = new ListConfigurationEntriesOptions();
+         return options.name(name);
+      }
+
+      /**
+       * @see ListConfigurationEntriesOptions#page
+       */
+      public static ListConfigurationEntriesOptions page(long page) {
+         ListConfigurationEntriesOptions options = new ListConfigurationEntriesOptions();
+         return options.page(page);
+      }
+
+      /**
+       * @see ListConfigurationEntriesOptions#pageSize
+       */
+      public static ListConfigurationEntriesOptions pageSize(long pageSize) {
+         ListConfigurationEntriesOptions options = new ListConfigurationEntriesOptions();
+         return options.pageSize(pageSize);
+      }
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListFirewallRulesOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListFirewallRulesOptions.java
new file mode 100644
index 0000000..4b683e9
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListFirewallRulesOptions.java
@@ -0,0 +1,147 @@
+/**
+ * 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.cloudstack.options;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Options used to control what firewall rules are returned
+ * 
+ * @see <a href=
+ *      "http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/listFirewallRules.html"
+ *      />
+ * @author Andrei Savu
+ */
+public class ListFirewallRulesOptions extends AccountInDomainOptions {
+
+   public static final ListFirewallRulesOptions NONE = new ListFirewallRulesOptions();
+
+   /**
+    * @param id
+    *    firewall rule ID
+    */
+   public ListFirewallRulesOptions id(long id) {
+      this.queryParameters.replaceValues("id", ImmutableSet.of(id + ""));
+      return this;
+   }
+
+   /**
+    * @param ipAddressId
+    *    the id of IP address of the firwall services
+    */
+   public ListFirewallRulesOptions ipAddressId(long ipAddressId) {
+      this.queryParameters.replaceValues("ipaddressid", ImmutableSet.of(ipAddressId + ""));
+      return this;
+   }
+
+   /**
+    * @param keyword
+    *    list by keyword
+    */
+   public ListFirewallRulesOptions keyword(String keyword) {
+      this.queryParameters.replaceValues("keyword", ImmutableSet.of(keyword));
+      return this;
+   }
+
+   public ListFirewallRulesOptions page(long page) {
+      this.queryParameters.replaceValues("page", ImmutableSet.of(page + ""));
+      return this;
+   }
+
+   public ListFirewallRulesOptions pageSize(long pageSize) {
+      this.queryParameters.replaceValues("pagesize", ImmutableSet.of(pageSize + ""));
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see ListFirewallRulesOptions#id
+       */
+      public static ListFirewallRulesOptions id(long id) {
+         ListFirewallRulesOptions options = new ListFirewallRulesOptions();
+         return options.id(id);
+      }
+
+      /**
+       * @see ListFirewallRulesOptions#ipAddressId
+       */
+      public static ListFirewallRulesOptions ipAddressId(long ipAddressId) {
+         ListFirewallRulesOptions options = new ListFirewallRulesOptions();
+         return options.ipAddressId(ipAddressId);
+      }
+
+      /**
+       * @see ListFirewallRulesOptions#keyword
+       */
+      public static ListFirewallRulesOptions keyword(String keyword) {
+         ListFirewallRulesOptions options = new ListFirewallRulesOptions();
+         return options.keyword(keyword);
+      }
+
+      /**
+       * @see ListFirewallRulesOptions#page
+       */
+      public static ListFirewallRulesOptions page(long page) {
+         ListFirewallRulesOptions options = new ListFirewallRulesOptions();
+         return options.page(page);
+      }
+
+      /**
+       * @see ListFirewallRulesOptions#pageSize
+       */
+      public static ListFirewallRulesOptions pageSize(long pageSize) {
+         ListFirewallRulesOptions options = new ListFirewallRulesOptions();
+         return options.pageSize(pageSize);
+      }
+
+      /**
+       * @see ListFirewallRulesOptions#accountInDomain
+       */
+      public static ListFirewallRulesOptions accountInDomain(String account, long domain) {
+         ListFirewallRulesOptions options = new ListFirewallRulesOptions();
+         return options.accountInDomain(account, domain);
+      }
+
+      /**
+       * @see ListFirewallRulesOptions#domainId
+       */
+      public static ListFirewallRulesOptions domainId(long id) {
+         ListFirewallRulesOptions options = new ListFirewallRulesOptions();
+         return options.domainId(id);
+      }
+
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public ListFirewallRulesOptions accountInDomain(String account, long domain) {
+      return ListFirewallRulesOptions.class.cast(super.accountInDomain(account, domain));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public ListFirewallRulesOptions domainId(long domainId) {
+      return ListFirewallRulesOptions.class.cast(super.domainId(domainId));
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListISOsOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListISOsOptions.java
index f3fdef4..cabb8aa 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListISOsOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListISOsOptions.java
@@ -1,4 +1,22 @@
 /**
+ * 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.
+ */
+/**
    * Licensed to jclouds, Inc. (jclouds) under one or more
    * contributor license agreements.  See the NOTICE file
    * distributed with this work for additional information
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListLoadBalancerRulesOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListLoadBalancerRulesOptions.java
index 72f0619..5ca3882 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListLoadBalancerRulesOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListLoadBalancerRulesOptions.java
@@ -66,7 +66,30 @@
    public ListLoadBalancerRulesOptions virtualMachineId(long virtualMachineId) {
       this.queryParameters.replaceValues("virtualmachineid", ImmutableSet.of(virtualMachineId + ""));
       return this;
+   }
 
+   /**
+    * @param zoneId the availability zone ID
+    */
+   public ListLoadBalancerRulesOptions zoneId(long zoneId) {
+      this.queryParameters.replaceValues("zoneid", ImmutableSet.of(zoneId + ""));
+      return this;
+   }
+
+   /**
+    * @param page the number of the page
+    */
+   public ListLoadBalancerRulesOptions page(long page) {
+      this.queryParameters.replaceValues("page", ImmutableSet.of(page + ""));
+      return this;
+   }
+
+   /**
+    * @param pageSize
+    */
+   public ListLoadBalancerRulesOptions pageSize(long pageSize) {
+      this.queryParameters.replaceValues("pagesize", ImmutableSet.of(pageSize + ""));
+      return this;
    }
 
    public static class Builder {
@@ -118,6 +141,30 @@
          ListLoadBalancerRulesOptions options = new ListLoadBalancerRulesOptions();
          return options.virtualMachineId(virtualMachineId);
       }
+
+      /**
+       * @see ListLoadBalancerRulesOptions#zoneId
+       */
+      public static ListLoadBalancerRulesOptions zoneId(long zoneId) {
+         ListLoadBalancerRulesOptions options = new ListLoadBalancerRulesOptions();
+         return options.zoneId(zoneId);
+      }
+
+      /**
+       * @see ListLoadBalancerRulesOptions#page
+       */
+      public static ListLoadBalancerRulesOptions page(long page) {
+         ListLoadBalancerRulesOptions options = new ListLoadBalancerRulesOptions();
+         return options.page(page);
+      }
+
+      /**
+       * @see ListLoadBalancerRulesOptions#pageSize
+       */
+      public static ListLoadBalancerRulesOptions pageSize(long pageSize) {
+         ListLoadBalancerRulesOptions options = new ListLoadBalancerRulesOptions();
+         return options.pageSize(pageSize);
+      }
    }
 
    /**
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListPortForwardingRulesOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListPortForwardingRulesOptions.java
index c9b31f0..5cf580b 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListPortForwardingRulesOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListPortForwardingRulesOptions.java
@@ -22,27 +22,53 @@
 
 /**
  * Options used to control what port forwarding rules are returned
- * 
- * @see <a href=
- *      "http://download.cloud.com/releases/2.2.0/api/user/listIpForwardingRules.html"
- *      />
+ *
  * @author Adrian Cole
+ * @see <a href=
+ *      "http://download.cloud.com/releases/2.2.0/api_2.2.12/global_admin/listPortForwardingRules.html"
+ *      />
  */
 public class ListPortForwardingRulesOptions extends AccountInDomainOptions {
 
    public static final ListPortForwardingRulesOptions NONE = new ListPortForwardingRulesOptions();
 
    /**
-    * @param IPAddressId
-    *           list the rule belonging to this public ip address
+    * @param id
+    *       lists rule with the specified ID
     */
-   public ListPortForwardingRulesOptions IPAddressId(long IPAddressId) {
+   public ListPortForwardingRulesOptions id(long id) {
+      this.queryParameters.replaceValues("id", ImmutableSet.of(id + ""));
+      return this;
+   }
+
+   /**
+    * @param IPAddressId
+    *       list the rule belonging to this public ip address
+    */
+   public ListPortForwardingRulesOptions ipAddressId(long IPAddressId) {
       this.queryParameters.replaceValues("ipaddressid", ImmutableSet.of(IPAddressId + ""));
       return this;
 
    }
 
    public static class Builder {
+
+      /**
+       * @see ListPortForwardingRulesOptions#id
+       */
+      public static ListPortForwardingRulesOptions id(long id) {
+         ListPortForwardingRulesOptions options = new ListPortForwardingRulesOptions();
+         return options.id(id);
+      }
+
+      /**
+       * @see ListPortForwardingRulesOptions#ipAddressId
+       */
+      public static ListPortForwardingRulesOptions ipAddressId(long ipAddressId) {
+         ListPortForwardingRulesOptions options = new ListPortForwardingRulesOptions();
+         return options.ipAddressId(ipAddressId);
+      }
+
       /**
        * @see ListPortForwardingRulesOptions#accountInDomain
        */
@@ -52,21 +78,12 @@
       }
 
       /**
-       * @see ListPortForwardingRulesOptions#IPAddressId
-       */
-      public static ListPortForwardingRulesOptions IPAddressId(long IPAddressId) {
-         ListPortForwardingRulesOptions options = new ListPortForwardingRulesOptions();
-         return options.IPAddressId(IPAddressId);
-      }
-
-      /**
        * @see ListPortForwardingRulesOptions#domainId
        */
       public static ListPortForwardingRulesOptions domainId(long id) {
          ListPortForwardingRulesOptions options = new ListPortForwardingRulesOptions();
          return options.domainId(id);
       }
-
    }
 
    /**
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListSnapshotPoliciesOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListSnapshotPoliciesOptions.java
index 790ba6f..9e36f46 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListSnapshotPoliciesOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListSnapshotPoliciesOptions.java
@@ -1,4 +1,22 @@
 /**
+ * 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.
+ */
+/**
    * Licensed to jclouds, Inc. (jclouds) under one or more
    * contributor license agreements.  See the NOTICE file
    * distributed with this work for additional information
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListSnapshotsOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListSnapshotsOptions.java
index 6a4ef54..11d32e6 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListSnapshotsOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/ListSnapshotsOptions.java
@@ -1,4 +1,22 @@
 /**
+ * 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.
+ */
+/**
    * Licensed to jclouds, Inc. (jclouds) under one or more
    * contributor license agreements.  See the NOTICE file
    * distributed with this work for additional information
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/RegisterISOOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/RegisterISOOptions.java
index e1c5f53..8482e6e 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/RegisterISOOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/RegisterISOOptions.java
@@ -1,4 +1,22 @@
 /**
+ * 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.
+ */
+/**
    * Licensed to jclouds, Inc. (jclouds) under one or more
    * contributor license agreements.  See the NOTICE file
    * distributed with this work for additional information
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/UpdateISOOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/UpdateISOOptions.java
index 1390f01..397c5f4 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/UpdateISOOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/UpdateISOOptions.java
@@ -1,4 +1,22 @@
 /**
+ * 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.
+ */
+/**
    * Licensed to jclouds, Inc. (jclouds) under one or more
    * contributor license agreements.  See the NOTICE file
    * distributed with this work for additional information
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/UpdateLoadBalancerRuleOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/UpdateLoadBalancerRuleOptions.java
new file mode 100644
index 0000000..e2613e1
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/UpdateLoadBalancerRuleOptions.java
@@ -0,0 +1,90 @@
+/**
+ * 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.cloudstack.options;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudstack.domain.LoadBalancerRule;
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+import java.util.Set;
+
+/**
+ * Options used to control how a load balancer rule is updated
+ *
+ * @author Andrei Savu
+ * @see <a href=
+ *      "http://download.cloud.com/releases/2.2.0/api_2.2.12/user/updateLoadBalancerRule.html"
+ *      />
+ */
+public class UpdateLoadBalancerRuleOptions extends BaseHttpRequestOptions {
+
+   public static final UpdateLoadBalancerRuleOptions NONE = new UpdateLoadBalancerRuleOptions();
+
+   /**
+    * @param algorithm load balancer algorithm (source, roundrobin, leastconn)
+    */
+   public UpdateLoadBalancerRuleOptions algorithm(LoadBalancerRule.Algorithm algorithm) {
+      this.queryParameters.replaceValues("algorithm", ImmutableSet.of(algorithm.toString()));
+      return this;
+   }
+
+   /**
+    * @param description the description of the load balancer rule
+    */
+   public UpdateLoadBalancerRuleOptions description(String description) {
+      this.queryParameters.replaceValues("description", ImmutableSet.of(description));
+      return this;
+   }
+
+   /**
+    * @param name the name of the load balancer rule
+    */
+   public UpdateLoadBalancerRuleOptions name(String name) {
+      this.queryParameters.replaceValues("name", ImmutableSet.of(name));
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see UpdateLoadBalancerRuleOptions#algorithm
+       */
+      public static UpdateLoadBalancerRuleOptions algorithm(LoadBalancerRule.Algorithm algorithm) {
+         UpdateLoadBalancerRuleOptions options = new UpdateLoadBalancerRuleOptions();
+         return options.algorithm(algorithm);
+      }
+
+      /**
+       * @see UpdateLoadBalancerRuleOptions#description
+       */
+      public static UpdateLoadBalancerRuleOptions description(String description) {
+         UpdateLoadBalancerRuleOptions options = new UpdateLoadBalancerRuleOptions();
+         return options.description(description);
+      }
+
+      /**
+       * @see UpdateLoadBalancerRuleOptions#name
+       */
+      public static UpdateLoadBalancerRuleOptions name(String name) {
+         UpdateLoadBalancerRuleOptions options = new UpdateLoadBalancerRuleOptions();
+         return options.name(name);
+      }
+   }
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/NetworkPredicates.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/NetworkPredicates.java
index 10119c8..9f69f02 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/NetworkPredicates.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/NetworkPredicates.java
@@ -93,6 +93,19 @@
       }
    }
 
+   private static class DefaultNetworkInZone implements Predicate<Network> {
+      private final long zoneId;
+
+      public DefaultNetworkInZone(long zoneId) {
+         this.zoneId = zoneId;
+      }
+
+      @Override
+      public boolean apply(Network network) {
+         return network.getZoneId() == zoneId && network.isDefault();
+      }
+   }
+
    public static class NetworkServiceNamed implements Predicate<NetworkService> {
       private final String name;
 
@@ -190,6 +203,16 @@
    }
 
    /**
+    * Filters for default networks in a specific zone.
+    *
+    * @param zoneId the ID of the required zone.
+    * @return networks in the zone that have the default flag set.
+    */
+   public static Predicate<Network> defaultNetworkInZone(final long zoneId) {
+      return new DefaultNetworkInZone(zoneId);
+   }
+
+   /**
     * 
     * @return always returns true.
     */
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/suppliers/ZoneIdToZoneSupplier.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/suppliers/ZoneIdToZoneSupplier.java
new file mode 100644
index 0000000..fd45408
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/suppliers/ZoneIdToZoneSupplier.java
@@ -0,0 +1,50 @@
+/**
+ * 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.cloudstack.suppliers;
+
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.inject.Inject;
+import org.jclouds.cloudstack.domain.Zone;
+
+import javax.inject.Named;
+import java.util.concurrent.TimeUnit;
+
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+
+/**
+ * Supplies a cache that maps from zone IDs to zones.
+ *
+ * @author Richard Downer
+ */
+public class ZoneIdToZoneSupplier implements Supplier<LoadingCache<Long, Zone>> {
+   private final LoadingCache<Long, Zone> cache;
+
+   @Inject
+   public ZoneIdToZoneSupplier(CacheLoader<Long, Zone> zoneIdToZone, @Named(PROPERTY_SESSION_INTERVAL) long expirationSecs) {
+      cache = CacheBuilder.newBuilder().expireAfterWrite(expirationSecs, TimeUnit.SECONDS).build(zoneIdToZone);
+   }
+
+   @Override
+   public LoadingCache<Long, Zone> get() {
+      return cache;
+   }
+}
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/strategy/OptionsConverterTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/strategy/OptionsConverterTest.java
new file mode 100644
index 0000000..af11e90
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/strategy/OptionsConverterTest.java
@@ -0,0 +1,121 @@
+/**
+ * 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.cloudstack.compute.strategy;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.Network;
+import org.jclouds.cloudstack.domain.NetworkService;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
+import org.testng.annotations.Test;
+
+import java.util.Collections;
+import java.util.Map;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+@Test(singleThreaded = true, testName="OptionsConverterTest")
+public class OptionsConverterTest {
+
+   private static final Map<Long,Network> EMPTY_NETWORKS_MAP = Collections.<Long, Network>emptyMap();
+   private static final int ZONE_ID = 2;
+   private final NetworkService firewallServiceWithStaticNat
+      = new NetworkService("Firewall", ImmutableMap.of("StaticNat", "true"));
+
+   @Test
+   public void testBasicNetworkOptionsConverter() {
+      BasicNetworkOptionsConverter converter = new BasicNetworkOptionsConverter();
+
+      CloudStackTemplateOptions optionsIn = CloudStackTemplateOptions.Builder.securityGroupId(42).networkId(46);
+      DeployVirtualMachineOptions optionsOut = new DeployVirtualMachineOptions();
+
+      DeployVirtualMachineOptions optionsOut2 = converter.apply(optionsIn, EMPTY_NETWORKS_MAP, ZONE_ID, optionsOut);
+      assertTrue(optionsOut == optionsOut2);
+
+      DeployVirtualMachineOptions optionsExpected = DeployVirtualMachineOptions.Builder.securityGroupId(42).networkId(46);
+      assertEquals(optionsOut, optionsExpected);
+   }
+
+   @Test
+   public void testAdvancedSecurityGroupsNotAllowed() {
+      boolean exceptionThrown = false;
+      AdvancedNetworkOptionsConverter converter = new AdvancedNetworkOptionsConverter();
+      CloudStackTemplateOptions optionsIn = CloudStackTemplateOptions.Builder.securityGroupId(42);
+
+      try {
+         converter.apply(optionsIn, EMPTY_NETWORKS_MAP, ZONE_ID, DeployVirtualMachineOptions.NONE);
+      } catch(IllegalArgumentException e) {
+         exceptionThrown = true;
+      }
+
+      assertTrue(exceptionThrown, "IllegalArgumentException should have been thrown");
+   }
+
+   @Test
+   public void testAdvancedExplicitNetworkSelection() {
+      AdvancedNetworkOptionsConverter converter = new AdvancedNetworkOptionsConverter();
+      DeployVirtualMachineOptions optionsActual = converter.apply(CloudStackTemplateOptions.Builder.networkId(42),
+         EMPTY_NETWORKS_MAP, ZONE_ID, DeployVirtualMachineOptions.NONE);
+      DeployVirtualMachineOptions optionsExpected = DeployVirtualMachineOptions.Builder.networkId(42);
+      assertEquals(optionsActual, optionsExpected);
+   }
+
+   @Test
+   public void testAdvancedAutoDetectNetwork() {
+      AdvancedNetworkOptionsConverter converter = new AdvancedNetworkOptionsConverter();
+
+      Network eligibleNetwork = Network.builder()
+         .id(25).zoneId(ZONE_ID).isDefault(true).services(ImmutableSet.of(firewallServiceWithStaticNat))
+         .build();
+      DeployVirtualMachineOptions optionsActual = converter.apply(CloudStackTemplateOptions.NONE,
+         ImmutableMap.of(eligibleNetwork.getId(), eligibleNetwork), ZONE_ID, DeployVirtualMachineOptions.NONE);
+      DeployVirtualMachineOptions optionsExpected = DeployVirtualMachineOptions.Builder.networkId(25);
+      assertEquals(optionsActual, optionsExpected);
+   }
+
+   @Test
+   public void testAdvancedWhenNoNetworkGiven() {
+      AdvancedNetworkOptionsConverter converter = new AdvancedNetworkOptionsConverter();
+      boolean exceptionThrown = false;
+      try {
+         converter.apply(CloudStackTemplateOptions.NONE, EMPTY_NETWORKS_MAP, ZONE_ID, DeployVirtualMachineOptions.NONE);
+      } catch(IllegalArgumentException e) {
+         exceptionThrown = true;
+      }
+      assertTrue(exceptionThrown);
+   }
+
+   @Test
+   public void testAdvancedWhenNoNetworkEligible() {
+      AdvancedNetworkOptionsConverter converter = new AdvancedNetworkOptionsConverter();
+      Network unsuitableNetwork = Network.builder()
+         .id(25).zoneId(ZONE_ID)
+         .build();
+
+      boolean exceptionThrown = false;
+      try {
+         converter.apply(CloudStackTemplateOptions.NONE, ImmutableMap.of(unsuitableNetwork.getId(), unsuitableNetwork), ZONE_ID, DeployVirtualMachineOptions.NONE);
+      } catch(IllegalArgumentException e) {
+         exceptionThrown = true;
+      }
+      assertTrue(exceptionThrown);
+   }
+}
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientExpectTest.java
new file mode 100644
index 0000000..93f0b1b
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientExpectTest.java
@@ -0,0 +1,114 @@
+/**
+ * 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.cloudstack.features;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.Set;
+
+import org.jclouds.cloudstack.CloudStackContext;
+import org.jclouds.cloudstack.domain.Account;
+import org.jclouds.cloudstack.domain.User;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Test the CloudStack AccountClient
+ *
+ * @author Andrei Savu
+ */
+@Test(groups = "unit", testName = "AccountClientExpectTest")
+public class AccountClientExpectTest extends BaseCloudStackRestClientExpectTest<AccountClient> {
+
+
+   public void testListAccountsWhenResponseIs2xx() {
+
+      AccountClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&command=listAccounts&apiKey=identity&signature=maSZcp9ivkL7osVh87qxlrYbZC8%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/listaccountsresponse.json"))
+            .build());
+
+      Set<User> users = ImmutableSet.of(
+         User.builder()
+            .id(505)
+            .name("jclouds")
+            .firstName("Adrian")
+            .lastName("Cole")
+            .email("adrian@jclouds.org")
+            .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-04-19T01:57:24+0000"))
+            .state(User.State.ENABLED)
+            .account("jclouds")
+            .accountType(Account.Type.USER)
+            .domainId(457)
+            .domain("AA000062-jclouds-dev")
+            .apiKey("APIKEY")
+            .secretKey("SECRETKEY").build());
+
+      assertEquals(client.listAccounts(),
+         ImmutableSet.of(Account.builder()
+            .id(505)
+            .name("jclouds")
+            .type(Account.Type.USER)
+            .domainId(457)
+            .domain("AA000062-jclouds-dev")
+            .receivedBytes(318900216)
+            .sentBytes(23189677)
+            .VMLimit(15l)
+            .VMs(1)
+            .IPsAvailable(14l)
+            .IPLimit(15l)
+            .IPs(0)
+            .IPsAvailable(15l)
+            .volumeLimit(90l)
+            .volumes(2)
+            .volumesAvailable(88l)
+            .snapshotLimit(250l)
+            .snapshots(0)
+            .snapshotsAvailable(250l)
+            .templateLimit(15l)
+            .templates(0)
+            .templatesAvailable(15l)
+            .VMsAvailable(14l)
+            .VMsStopped(0)
+            .VMsRunning(1)
+            .state(Account.State.ENABLED)
+            .users(users).build()));
+   }
+
+   @Override
+   protected AccountClient clientFrom(CloudStackContext context) {
+      return context.getProviderSpecificContext().getApi().getAccountClient();
+   }
+}
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java
new file mode 100644
index 0000000..4f05a35
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java
@@ -0,0 +1,54 @@
+/**
+ * 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.cloudstack.features;
+
+import java.util.Properties;
+
+import org.jclouds.cloudstack.CloudStackContext;
+import org.jclouds.compute.ComputeServiceContextFactory;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.logging.config.NullLoggingModule;
+import org.jclouds.rest.BaseRestClientExpectTest;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+/**
+ * Base class for writing CloudStack Rest Client Expect tests
+ * 
+ * @author Andrei Savu
+ */
+public abstract class BaseCloudStackRestClientExpectTest<S> extends BaseRestClientExpectTest<S> {
+
+   public BaseCloudStackRestClientExpectTest() {
+      provider = "cloudstack";
+   }
+
+   @Override
+   public S createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
+      return clientFrom(CloudStackContext.class.cast(new ComputeServiceContextFactory(setupRestProperties())
+               .createContext(provider, "identity", "credential", ImmutableSet.<Module> of(new ExpectModule(fn),
+                        new NullLoggingModule(), module), props)));
+   }
+
+   protected abstract S clientFrom(CloudStackContext context);
+
+}
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallAsyncClientTest.java
index a40ff93..1bc2025 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.domain.PortForwardingRule;
 import org.jclouds.cloudstack.options.ListPortForwardingRulesOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
@@ -64,7 +65,7 @@
    public void testListPortForwardingRulesOptions() throws SecurityException, NoSuchMethodException, IOException {
       Method method = FirewallAsyncClient.class.getMethod("listPortForwardingRules",
             ListPortForwardingRulesOptions[].class);
-      HttpRequest httpRequest = processor.createRequest(method, ListPortForwardingRulesOptions.Builder.IPAddressId(3));
+      HttpRequest httpRequest = processor.createRequest(method, ListPortForwardingRulesOptions.Builder.ipAddressId(3));
 
       assertRequestLineEquals(httpRequest,
             "GET http://localhost:8080/client/api?response=json&command=listPortForwardingRules&ipaddressid=3 HTTP/1.1");
@@ -82,12 +83,12 @@
    public void testCreatePortForwardingRuleForVirtualMachine() throws SecurityException, NoSuchMethodException,
          IOException {
       Method method = FirewallAsyncClient.class.getMethod("createPortForwardingRuleForVirtualMachine", long.class,
-            long.class, String.class, int.class, int.class);
-      HttpRequest httpRequest = processor.createRequest(method, 6, 7, "tcp", 22, 22);
+            PortForwardingRule.Protocol.class, int.class, long.class, int.class);
+      HttpRequest httpRequest = processor.createRequest(method, 6L, PortForwardingRule.Protocol.TCP, 22, 7L, 22);
 
       assertRequestLineEquals(
             httpRequest,
-            "GET http://localhost:8080/client/api?response=json&command=createPortForwardingRule&virtualmachineid=6&protocol=tcp&ipaddressid=7&privateport=22&publicport=22 HTTP/1.1");
+            "GET http://localhost:8080/client/api?response=json&command=createPortForwardingRule&ipaddressid=6&publicport=22&protocol=tcp&virtualmachineid=7&privateport=22 HTTP/1.1");
       assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
       assertPayloadEquals(httpRequest, null, null, false);
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientExpectTest.java
new file mode 100644
index 0000000..e663222
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientExpectTest.java
@@ -0,0 +1,315 @@
+/**
+ * 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.cloudstack.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.net.URI;
+import java.util.Set;
+
+import org.jclouds.cloudstack.CloudStackContext;
+import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.FirewallRule;
+import org.jclouds.cloudstack.domain.PortForwardingRule;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Test the CloudStack FirewallClient
+ *
+ * @author Andrei Savu
+ */
+@Test(groups = "unit", testName = "FirewallClientExpectTest")
+public class FirewallClientExpectTest extends BaseCloudStackRestClientExpectTest<FirewallClient> {
+
+   public void testListFirewallRulesWhenResponseIs2xx() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&command=listFirewallRules&" +
+                  "apiKey=identity&signature=MktZKKH3USVKiC9SlYTSHMCaCcg%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/listfirewallrulesresponse.json"))
+            .build());
+
+      Set<String> CIDRs  = ImmutableSet.of("0.0.0.0/0");
+      assertEquals(client.listFirewallRules(),
+         ImmutableSet.of(
+            FirewallRule.builder().id(2017).protocol(FirewallRule.Protocol.TCP).startPort(30)
+               .endPort(35).ipAddressId(2).ipAddress("10.27.27.51").state(FirewallRule.State.ACTIVE)
+               .CIDRs(CIDRs).build(),
+            FirewallRule.builder().id(2016).protocol(FirewallRule.Protocol.TCP).startPort(22)
+               .endPort(22).ipAddressId(2).ipAddress("10.27.27.51").state(FirewallRule.State.ACTIVE)
+               .CIDRs(CIDRs).build(),
+            FirewallRule.builder().id(10).protocol(FirewallRule.Protocol.TCP).startPort(22)
+               .endPort(22).ipAddressId(8).ipAddress("10.27.27.57").state(FirewallRule.State.ACTIVE)
+               .CIDRs(CIDRs).build()
+         ));
+   }
+
+   public void testListFirewallRulesWhenReponseIs404() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&command=listFirewallRules&" +
+                  "apiKey=identity&signature=MktZKKH3USVKiC9SlYTSHMCaCcg%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(404)
+            .build());
+
+      assertEquals(client.listFirewallRules(), ImmutableSet.of());
+   }
+
+   public void testGetFirewallRuleWhenResponseIs2xx() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&command=listFirewallRules&" +
+                  "id=2017&apiKey=identity&signature=0r5iL%2Bzix9rmD07lJIOhY68mYY0%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/getfirewallrulesresponse.json"))
+            .build());
+
+      assertEquals(client.getFirewallRule(2017),
+         FirewallRule.builder().id(2017).protocol(FirewallRule.Protocol.TCP).startPort(30)
+            .endPort(35).ipAddressId(2).ipAddress("10.27.27.51").state(FirewallRule.State.ACTIVE)
+            .CIDRs(ImmutableSet.of("0.0.0.0/0")).build()
+      );
+   }
+
+   public void testGetFirewallRuleWhenResponseIs404() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&command=listFirewallRules&" +
+                  "id=4&apiKey=identity&signature=PPX5U9kmaS116SgG4Ihf8xK%2BcSE%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(404)
+            .build());
+
+      assertNull(client.getFirewallRule(4));
+   }
+
+   public void testCreateFirewallRuleForIpAndProtocol() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&command=createFirewallRule&" +
+                  "ipaddressid=2&protocol=TCP&apiKey=identity&signature=d0MZ%2FyhQPAaV%2BYQmfZsQtQL2C28%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/createfirewallrulesresponse.json"))
+            .build());
+
+      AsyncCreateResponse response = client.createFirewallRuleForIpAndProtocol(2, FirewallRule.Protocol.TCP);
+      assertEquals(response.getJobId(), 2036);
+      assertEquals(response.getId(), 2017);
+   }
+
+   public void testDeleteFirewallRule() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=deleteFirewallRule&id=2015&apiKey=identity&signature=%2FT5FAO2yGPctaPmg7TEtIEFW3EU%3D"))
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/deletefirewallrulesresponse.json"))
+            .build());
+
+      client.deleteFirewallRule(2015);
+   }
+
+   public void testListPortForwardingRulesWhenResponseIs2xx() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=listPortForwardingRules&apiKey=identity&signature=YFBu1VOSkiDKxm0K42sIXJWy%2BBo%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/listportforwardingrulesresponse.json"))
+            .build());
+
+      Set<String> cidrs = ImmutableSet.of("0.0.0.0/1", "128.0.0.0/1");
+
+      assertEquals(client.listPortForwardingRules(),
+         ImmutableSet.<PortForwardingRule>of(
+            PortForwardingRule.builder().id(15).privatePort(22).protocol(PortForwardingRule.Protocol.TCP)
+               .publicPort(2022).virtualMachineId(3).virtualMachineName("i-3-3-VM").IPAddressId(3)
+               .IPAddress("72.52.126.32").state(PortForwardingRule.State.ACTIVE).CIDRs(cidrs).build(),
+            PortForwardingRule.builder().id(18).privatePort(22).protocol(PortForwardingRule.Protocol.TCP)
+               .publicPort(22).virtualMachineId(89).virtualMachineName("i-3-89-VM").IPAddressId(34)
+               .IPAddress("72.52.126.63").state(PortForwardingRule.State.ACTIVE).build())
+      );
+   }
+
+   public void testListPortForwardingRulesWhenReponseIs404() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=listPortForwardingRules&apiKey=identity&signature=YFBu1VOSkiDKxm0K42sIXJWy%2BBo%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(404)
+            .build());
+
+      assertEquals(client.listPortForwardingRules(), ImmutableSet.of());
+   }
+
+   public void testGetPortForwardingRuleWhenResponseIs2xx() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=listPortForwardingRules&id=15&apiKey=identity&signature=ABJsciF4n2tXaiyUmEvc3oYh9MA%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/getportforwardingrulesresponse.json"))
+            .build());
+
+      Set<String> cidrs = ImmutableSet.of("0.0.0.0/1", "128.0.0.0/1");
+
+      assertEquals(client.getPortForwardingRule(15),
+         PortForwardingRule.builder().id(15).privatePort(22).protocol(PortForwardingRule.Protocol.TCP)
+            .publicPort(2022).virtualMachineId(3).virtualMachineName("i-3-3-VM").IPAddressId(3)
+            .IPAddress("72.52.126.32").state(PortForwardingRule.State.ACTIVE).CIDRs(cidrs).build());
+   }
+
+   public void testGetPortForwardingRuleWhenResponseIs404() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=listPortForwardingRules&id=4&apiKey=identity&signature=CTOmmIOGIiZx0YATqh%2FFk0zIplw%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(404)
+            .build());
+
+      assertNull(client.getPortForwardingRule(4));
+   }
+
+   public void testCreatePortForwardingRuleForVirtualMachine() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&command=createPortForwardingRule&" +
+                  "ipaddressid=2&publicport=22&protocol=tcp&virtualmachineid=1234&privateport=22&" +
+                  "apiKey=identity&signature=84dtGzQp0G6k3z3Gkc3F%2FHBNS2Y%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/createportforwardingrulesresponse.json"))
+            .build());
+
+      AsyncCreateResponse response = client.createPortForwardingRuleForVirtualMachine(
+         2, PortForwardingRule.Protocol.TCP, 22, 1234, 22);
+      assertEquals(response.getJobId(), 2035);
+      assertEquals(response.getId(), 2015);
+   }
+
+   public void testDeletePortForwardingRule() {
+      FirewallClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=deletePortForwardingRule&id=2015&apiKey=identity&signature=2UE7KB3wm5ocmR%2BGMNFKPKfiDo8%3D"))
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/deleteportforwardingrulesresponse.json"))
+            .build());
+
+      client.deletePortForwardingRule(2015);
+   }
+   
+   @Override
+   protected FirewallClient clientFrom(CloudStackContext context) {
+      return context.getProviderSpecificContext().getApi().getFirewallClient();
+   }
+}
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
index 4ea0345..836e414 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
@@ -19,18 +19,22 @@
 package org.jclouds.cloudstack.features;
 
 import static com.google.common.collect.Iterables.find;
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.supportsPortForwarding;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
 import java.util.NoSuchElementException;
 import java.util.Set;
 
+import com.google.common.base.Predicates;
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.FirewallRule;
 import org.jclouds.cloudstack.domain.Network;
 import org.jclouds.cloudstack.domain.PortForwardingRule;
 import org.jclouds.cloudstack.domain.PublicIPAddress;
 import org.jclouds.cloudstack.domain.VirtualMachine;
-import org.jclouds.cloudstack.predicates.NetworkPredicates;
+import org.jclouds.cloudstack.options.CreateFirewallRuleOptions;
+import org.jclouds.logging.Logger;
 import org.jclouds.net.IPSocket;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
@@ -38,16 +42,21 @@
 
 import com.google.common.base.Predicate;
 
+import javax.annotation.Nullable;
+
 /**
  * Tests behavior of {@code FirewallClientLiveTest}
- * 
+ *
  * @author Adrian Cole
  */
 @Test(groups = "live", singleThreaded = true, testName = "FirewallClientLiveTest")
 public class FirewallClientLiveTest extends BaseCloudStackClientLiveTest {
    private PublicIPAddress ip = null;
    private VirtualMachine vm;
-   private PortForwardingRule rule;
+
+   private FirewallRule firewallRule;
+   private PortForwardingRule portForwardingRule;
+
    private Network network;
    private boolean networksDisabled;
 
@@ -56,13 +65,25 @@
       super.setupClient();
       prefix += "rule";
       try {
-         network = find(client.getNetworkClient().listNetworks(), NetworkPredicates.supportsPortForwarding());
+         network = find(client.getNetworkClient().listNetworks(), Predicates.and(supportsPortForwarding(),
+            new Predicate<Network>() {
+               @Override
+               public boolean apply(@Nullable Network network) {
+                  return network.isDefault()
+                     && !network.isSecurityGroupEnabled()
+                     && network.getAccount().equals(user.getAccount());
+               }
+            }));
+
          Long defaultTemplate = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
+
          vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network,
-               defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()), client, jobComplete,
-               virtualMachineRunning);
+            defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()),
+            client, jobComplete, virtualMachineRunning);
+
          if (vm.getPassword() != null && !loginCredentials.hasPasswordOption())
             loginCredentials = loginCredentials.toBuilder().password(vm.getPassword()).build();
+
       } catch (NoSuchElementException e) {
          networksDisabled = true;
       }
@@ -71,30 +92,75 @@
    public void testCreatePortForwardingRule() throws Exception {
       if (networksDisabled)
          return;
-      while (rule == null) {
+      while (portForwardingRule == null) {
          ip = reuseOrAssociate.apply(network);
          try {
-            AsyncCreateResponse job = client.getFirewallClient().createPortForwardingRuleForVirtualMachine(vm.getId(),
-                  ip.getId(), "tcp", 22, 22);
+            AsyncCreateResponse job = client.getFirewallClient()
+               .createPortForwardingRuleForVirtualMachine(ip.getId(), PortForwardingRule.Protocol.TCP, 22, vm.getId(), 22);
             assertTrue(jobComplete.apply(job.getJobId()));
-            rule = findRuleWithId(job.getId());
+            portForwardingRule = client.getFirewallClient().getPortForwardingRule(job.getId());
+
          } catch (IllegalStateException e) {
+            Logger.CONSOLE.error("Failed while trying to allocate ip: " + e);
             // very likely an ip conflict, so retry;
          }
       }
 
-      assertEquals(rule.getIPAddressId(), ip.getId());
-      assertEquals(rule.getVirtualMachineId(), vm.getId());
-      assertEquals(rule.getPublicPort(), 22);
-      assertEquals(rule.getProtocol(), "tcp");
-      checkRule(rule);
+      assertEquals(portForwardingRule.getIPAddressId(), ip.getId());
+      assertEquals(portForwardingRule.getVirtualMachineId(), vm.getId());
+      assertEquals(portForwardingRule.getPublicPort(), 22);
+      assertEquals(portForwardingRule.getProtocol(), "tcp");
+
+      checkPortForwardingRule(portForwardingRule);
       checkSSH(new IPSocket(ip.getIPAddress(), 22));
    }
 
+   @Test(dependsOnMethods = "testCreatePortForwardingRule")
+   public void testListPortForwardingRules() throws Exception {
+      Set<PortForwardingRule> response = client.getFirewallClient().listPortForwardingRules();
+      assert null != response;
+      assertTrue(response.size() >= 0);
+      for (final PortForwardingRule rule : response) {
+         checkPortForwardingRule(rule);
+      }
+   }
+
+   @Test(dependsOnMethods = "testCreatePortForwardingRule")
+   public void testCreateFirewallRule() {
+      if (networksDisabled)
+         return;
+
+      AsyncCreateResponse job = client.getFirewallClient().createFirewallRuleForIpAndProtocol(
+         ip.getId(), FirewallRule.Protocol.TCP, CreateFirewallRuleOptions.Builder.startPort(30).endPort(35));
+      assertTrue(jobComplete.apply(job.getJobId()));
+      firewallRule = client.getFirewallClient().getFirewallRule(job.getId());
+
+      assertEquals(firewallRule.getStartPort(), 30);
+      assertEquals(firewallRule.getEndPort(), 35);
+      assertEquals(firewallRule.getProtocol(), FirewallRule.Protocol.TCP);
+
+      checkFirewallRule(firewallRule);
+   }
+
+   @Test(dependsOnMethods = "testCreateFirewallRule")
+   public void testListFirewallRules() {
+      Set<FirewallRule> rules = client.getFirewallClient().listFirewallRules();
+
+      assert rules != null;
+      assertTrue(rules.size() > 0);
+
+      for(FirewallRule rule : rules) {
+         checkFirewallRule(rule);
+      }
+   }
+
    @AfterGroups(groups = "live")
    protected void tearDown() {
-      if (rule != null) {
-         client.getFirewallClient().deletePortForwardingRule(rule.getId());
+      if (firewallRule != null) {
+         client.getFirewallClient().deleteFirewallRule(firewallRule.getId());
+      }
+      if (portForwardingRule != null) {
+         client.getFirewallClient().deletePortForwardingRule(portForwardingRule.getId());
       }
       if (vm != null) {
          jobComplete.apply(client.getVirtualMachineClient().destroyVirtualMachine(vm.getId()));
@@ -105,30 +171,18 @@
       super.tearDown();
    }
 
-   public void testListPortForwardingRules() throws Exception {
-      Set<PortForwardingRule> response = client.getFirewallClient().listPortForwardingRules();
-      assert null != response;
-      assertTrue(response.size() >= 0);
-      for (final PortForwardingRule rule : response) {
-         PortForwardingRule newDetails = findRuleWithId(rule.getId());
-         assertEquals(rule.getId(), newDetails.getId());
-         checkRule(rule);
-      }
+   protected void checkFirewallRule(FirewallRule rule) {
+      assertEquals(rule,
+         client.getFirewallClient().getFirewallRule(rule.getId()));
+      assert rule.getId() > 0 : rule;
+      assert rule.getStartPort() > 0 : rule;
+      assert rule.getEndPort() >= rule.getStartPort() : rule;
+      assert rule.getProtocol() != null;
    }
 
-   private PortForwardingRule findRuleWithId(final long id) {
-      return find(client.getFirewallClient().listPortForwardingRules(), new Predicate<PortForwardingRule>() {
-
-         @Override
-         public boolean apply(PortForwardingRule arg0) {
-            return arg0.getId() == id;
-         }
-
-      });
-   }
-
-   protected void checkRule(PortForwardingRule rule) {
-      assertEquals(rule.getId(), findRuleWithId(rule.getId()).getId());
+   protected void checkPortForwardingRule(PortForwardingRule rule) {
+      assertEquals(rule,
+         client.getFirewallClient().getPortForwardingRule(rule.getId()));
       assert rule.getId() > 0 : rule;
       assert rule.getIPAddress() != null : rule;
       assert rule.getIPAddressId() > 0 : rule;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientExpectTest.java
new file mode 100644
index 0000000..913f021
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientExpectTest.java
@@ -0,0 +1,147 @@
+/**
+ * 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.cloudstack.features;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudstack.CloudStackClient;
+import org.jclouds.cloudstack.CloudStackContext;
+import org.jclouds.cloudstack.CloudStackGlobalClient;
+import org.jclouds.cloudstack.domain.AsyncCreateResponse;
+import org.jclouds.cloudstack.domain.ConfigurationEntry;
+import org.jclouds.cloudstack.domain.FirewallRule;
+import org.jclouds.cloudstack.domain.PortForwardingRule;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.rest.BaseRestClientExpectTest;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+import java.util.Set;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+/**
+ * Test the CloudStack GlobalConfigurationClient
+ *
+ * @author Andrei Savu
+ */
+@Test(groups = "unit", testName = "GlobalConfigurationClientExpectTest")
+public class GlobalConfigurationClientExpectTest extends BaseCloudStackRestClientExpectTest<GlobalConfigurationClient> {
+
+   @Test
+   public void testListConfigurationEntriesWhenResponseIs2xx() {
+      GlobalConfigurationClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=listConfigurations&apiKey=identity&signature=FUQCDbc4TH2S%2B7ExDgrOCqKI2bw%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/listconfigurationsresponse.json"))
+            .build());
+
+      assertEquals(client.listConfigurationEntries(),
+         ImmutableSet.of(
+            ConfigurationEntry.builder().category("Advanced").name("account.cleanup.interval").value("86400")
+               .description("The interval (in seconds) between cleanup for removed accounts").build(),
+            ConfigurationEntry.builder().category("Advanced").name("agent.lb.enabled").value("true")
+               .description("If agent load balancing enabled in cluster setup").build()
+         ));
+   }
+
+   @Test
+   public void testListConfigurationEntriesEmptyOn404() {
+      GlobalConfigurationClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=listConfigurations&apiKey=identity&signature=FUQCDbc4TH2S%2B7ExDgrOCqKI2bw%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(404)
+            .build());
+
+      assertEquals(client.listConfigurationEntries(), ImmutableSet.of());
+   }
+
+   @Test
+   public void testUpdateConfigurationEntryWhenResponseIs2xx() {
+      GlobalConfigurationClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=updateConfiguration&name=expunge.delay&value=11&" +
+                  "apiKey=identity&signature=I2yG35EhfgIXYObeLfU3cvf%2BPeE%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(200)
+            .payload(payloadFromResource("/updateconfigurationsresponse.json"))
+            .build());
+
+      assertEquals(client.updateConfigurationEntry("expunge.delay", "11"),
+         ConfigurationEntry.builder().category("Advanced").name("expunge.delay").value("11")
+            .description("Determines how long (in seconds) to wait before actually expunging " +
+               "destroyed vm. The default value = the default value of expunge.interval").build()
+      );
+   }
+
+   @Test
+   public void testUpdateConfigurationEntryNullOn404() {
+      GlobalConfigurationClient client = requestSendsResponse(
+         HttpRequest.builder()
+            .method("GET")
+            .endpoint(
+               URI.create("http://localhost:8080/client/api?response=json&" +
+                  "command=updateConfiguration&name=expunge.delay&value=11&" +
+                  "apiKey=identity&signature=I2yG35EhfgIXYObeLfU3cvf%2BPeE%3D"))
+            .headers(
+               ImmutableMultimap.<String, String>builder()
+                  .put("Accept", "application/json")
+                  .build())
+            .build(),
+         HttpResponse.builder()
+            .statusCode(404)
+            .build());
+
+      assertNull(client.updateConfigurationEntry("expunge.delay", "11"));
+   }
+
+   @Override
+   protected GlobalConfigurationClient clientFrom(CloudStackContext context) {
+      return context.getGlobalContext().getApi().getConfigurationClient();
+   }
+}
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java
new file mode 100644
index 0000000..2ad890f
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java
@@ -0,0 +1,102 @@
+/**
+ * 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.cloudstack.features;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import org.jclouds.cloudstack.domain.ConfigurationEntry;
+import org.testng.annotations.Test;
+import org.testng.collections.Sets;
+
+import javax.annotation.Nullable;
+import java.util.Set;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.jclouds.cloudstack.options.ListConfigurationEntriesOptions.Builder.name;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Tests behavior of {@code GlobalConfigurationClient}
+ *
+ * @author Andrei Savu
+ */
+@Test(groups = "live", singleThreaded = true, testName = "GlobalConfigurationClientLiveTest")
+public class GlobalConfigurationClientLiveTest extends BaseCloudStackClientLiveTest {
+
+   @Test
+   public void testListConfigurationEntries() {
+      assert globalAdminEnabled;
+
+      Set<ConfigurationEntry> entries = globalAdminClient
+         .getConfigurationClient().listConfigurationEntries();
+
+      Set<String> categories = Sets.newHashSet();
+      for (ConfigurationEntry entry : entries) {
+         checkConfigurationEntry(entry);
+         categories.add(entry.getCategory());
+      }
+
+      assert categories.containsAll(ImmutableSet.<Object>of("Network", "Advanced", "Premium",
+         "Storage", "Usage", "Snapshots", "Account Defaults", "Console Proxy", "Alert"));
+   }
+
+   @Test
+   public void testUpdateConfigurationEntry() {
+      assert globalAdminEnabled;
+
+      Set<ConfigurationEntry> entries = globalAdminClient
+         .getConfigurationClient().listConfigurationEntries();
+
+      long expungeDelay = Long.parseLong(getValueByName(entries, "expunge.delay"));
+      assert expungeDelay > 0;
+
+      globalAdminClient.getConfigurationClient()
+         .updateConfigurationEntry("expunge.delay", "" + (expungeDelay + 1));
+
+      long newDelay = Long.parseLong(getOnlyElement(globalAdminClient.getConfigurationClient()
+         .listConfigurationEntries(name("expunge.delay"))).getValue());
+      assertEquals(newDelay, expungeDelay + 1);
+
+      globalAdminClient.getConfigurationClient()
+         .updateConfigurationEntry("expunge.delay", "" + expungeDelay);
+   }
+
+   private void checkConfigurationEntry(ConfigurationEntry entry) {
+      assertEquals(entry, getEntryByName(globalAdminClient.getConfigurationClient()
+         .listConfigurationEntries(name(entry.getName())), entry.getName()));
+      assert entry.getCategory() != null : entry;
+      assert entry.getDescription() != null : entry;
+      assert entry.getName() != null : entry;
+   }
+
+   private String getValueByName(Set<ConfigurationEntry> entries, String name) {
+      return getEntryByName(entries, name).getValue();
+   }
+
+   private ConfigurationEntry getEntryByName(Set<ConfigurationEntry> entries, final String name) {
+      return Iterables.find(entries, new Predicate<ConfigurationEntry>() {
+         @Override
+         public boolean apply(@Nullable ConfigurationEntry entry) {
+            return entry != null && Objects.equal(name, entry.getName());
+         }
+      });
+   }
+}
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientLiveTest.java
index 7764ca8..cab4014 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientLiveTest.java
@@ -18,10 +18,13 @@
  */
 package org.jclouds.cloudstack.features;
 
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
 import java.util.Set;
 
+import com.google.common.base.Strings;
+import org.jclouds.cloudstack.domain.Cluster;
 import org.jclouds.cloudstack.domain.Host;
 import org.testng.annotations.Test;
 
@@ -61,4 +64,29 @@
       }
    }
 
+   @Test(groups = "live", enabled = true)
+   public void testListClusters() throws Exception {
+      assertTrue(globalAdminEnabled, "Test cannot run without global admin identity and credentials");
+
+      Set<Cluster> clusters = globalAdminClient.getHostClient().listClusters();
+      assert clusters.size() > 0 : clusters;
+
+      for(Cluster cluster : clusters) {
+         checkCluster(cluster);
+      }
+   }
+
+   private void checkCluster(Cluster cluster) {
+      assertTrue(cluster.getId() > 0);
+      assertFalse(Strings.isNullOrEmpty(cluster.getName()));
+      assertTrue(cluster.getAllocationState() != Host.AllocationState.UNKNOWN);
+      assertTrue(cluster.getClusterType() != Host.ClusterType.UNKNOWN);
+      assertFalse(Strings.isNullOrEmpty(cluster.getHypervisor()));
+      assertTrue(cluster.getManagedState() != Cluster.ManagedState.UNRECOGNIZED);
+      assertTrue(cluster.getPodId() > 0);
+      assertFalse(Strings.isNullOrEmpty(cluster.getPodName()));
+      assertTrue(cluster.getZoneId() > 0);
+      assertFalse(Strings.isNullOrEmpty(cluster.getZoneName()));
+   }
+
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClientTest.java
index 48b9278..a44842a 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClientTest.java
@@ -22,7 +22,9 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.domain.LoadBalancerRule.Algorithm;
+import org.jclouds.cloudstack.options.CreateLoadBalancerRuleOptions;
 import org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions;
+import org.jclouds.cloudstack.options.UpdateLoadBalancerRuleOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
 import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
@@ -80,7 +82,7 @@
 
    public void testCreateLoadBalancerRuleForPublicIP() throws SecurityException, NoSuchMethodException, IOException {
       Method method = LoadBalancerAsyncClient.class.getMethod("createLoadBalancerRuleForPublicIP", long.class,
-            Algorithm.class, String.class, int.class, int.class);
+            Algorithm.class, String.class, int.class, int.class, CreateLoadBalancerRuleOptions[].class);
       HttpRequest httpRequest = processor.createRequest(method, 6, Algorithm.LEASTCONN, "tcp", 22, 22);
 
       assertRequestLineEquals(
@@ -97,6 +99,22 @@
 
    }
 
+   public void testUpdateLoadBalancerRule() throws SecurityException, NoSuchMethodException, IOException {
+      Method method = LoadBalancerAsyncClient.class.getMethod("updateLoadBalancerRule", long.class, UpdateLoadBalancerRuleOptions[].class);
+      HttpRequest httpRequest = processor.createRequest(method, 5);
+
+      assertRequestLineEquals(httpRequest,
+            "GET http://localhost:8080/client/api?response=json&command=updateLoadBalancerRule&id=5 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertSaxResponseParserClassEquals(method, null);
+      assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
    public void testDeleteLoadBalancerRule() throws SecurityException, NoSuchMethodException, IOException {
       Method method = LoadBalancerAsyncClient.class.getMethod("deleteLoadBalancerRule", long.class);
       HttpRequest httpRequest = processor.createRequest(method, 5);
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
index 01931d7..4d258e8 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
@@ -19,6 +19,8 @@
 package org.jclouds.cloudstack.features;
 
 import static com.google.common.collect.Iterables.find;
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.hasLoadBalancerService;
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.isVirtualNetwork;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -48,6 +50,8 @@
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 
+import javax.annotation.Nullable;
+
 /**
  * Tests behavior of {@code LoadBalancerClientLiveTest}
  * 
@@ -71,7 +75,16 @@
       prefix += "rule";
       try {
          network = find(client.getNetworkClient().listNetworks(),
-               Predicates.and(NetworkPredicates.hasLoadBalancerService(), NetworkPredicates.isVirtualNetwork()));
+               Predicates.and(hasLoadBalancerService(), isVirtualNetwork(),
+                  new Predicate<Network>() {
+                     @Override
+                     public boolean apply(@Nullable Network network) {
+                        return network.isDefault()
+                           && !network.isSecurityGroupEnabled()
+                           && !network.isSystem()
+                           && network.getAccount().equals(user.getName());
+                     }
+                  }));
       } catch (NoSuchElementException e) {
          networksDisabled = true;
       }
@@ -82,8 +95,8 @@
          return;
       Long defaultTemplate = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
       vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network,
-            defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()), client, jobComplete,
-            virtualMachineRunning);
+            defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()),
+            client, jobComplete, virtualMachineRunning);
       if (vm.getPassword() != null && !loginCredentials.hasPasswordOption())
          loginCredentials = loginCredentials.toBuilder().password(vm.getPassword()).build();
    }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingClientLiveTest.java
index ffec261..eb4ec13 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingClientLiveTest.java
@@ -64,7 +64,7 @@
 
          } catch (NoSuchElementException e) {
             // This bug is present both in 2.2.8 and 2.2.12
-           assertTrue("2.2.8".equals(apiversion) || "2.2.12".equals(apiversion));
+           assertTrue("2.2.8".equals(apiVersion) || "2.2.12".equals(apiVersion));
          }
       }
    }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
index 3f355d2..fd44fd9 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
@@ -31,6 +31,7 @@
 import org.jclouds.cloudstack.domain.VirtualMachine;
 import org.jclouds.cloudstack.domain.Zone;
 import org.jclouds.cloudstack.options.AccountInDomainOptions;
+import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
 import org.jclouds.cloudstack.options.ListSecurityGroupsOptions;
 import org.jclouds.net.IPSocket;
 import org.jclouds.util.Strings2;
@@ -195,6 +196,23 @@
       assert group.getIngressRules() != null : group;
    }
 
+   @Test
+   public void testCreateVMWithoutSecurityGroupAssignsDefault() throws Exception {
+      if (!securityGroupsSupported)
+         return;
+      Long defaultTemplate = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
+      VirtualMachine newVm = VirtualMachineClientLiveTest.createVirtualMachineWithOptionsInZone(DeployVirtualMachineOptions.NONE,
+            zone.getId(), defaultTemplateOrPreferredInZone(defaultTemplate, client, zone.getId()), client,
+            jobComplete, virtualMachineRunning);
+      try {
+         VirtualMachine runningVm = client.getVirtualMachineClient().getVirtualMachine(newVm.getId());
+         assertTrue(runningVm.getSecurityGroups().size() == 1);
+         assertEquals(Iterables.getOnlyElement(runningVm.getSecurityGroups()).getName(), "default");
+      } finally {
+         assertTrue(jobComplete.apply(client.getVirtualMachineClient().destroyVirtualMachine(newVm.getId())));
+      }
+   }
+
    @AfterGroups(groups = "live")
    protected void tearDown() {
       if (vm != null) {
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/filters/QuerySignerTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/filters/QuerySignerTest.java
index c2d09ef..2e75496 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/filters/QuerySignerTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/filters/QuerySignerTest.java
@@ -21,17 +21,17 @@
 import static org.testng.Assert.assertEquals;
 
 import java.net.URI;
-import java.util.List;
-import java.util.Map;
 
 import org.jclouds.PropertiesBuilder;
 import org.jclouds.http.HttpRequest;
+import org.jclouds.http.IntegrationTestAsyncClient;
+import org.jclouds.http.IntegrationTestClient;
 import org.jclouds.logging.config.NullLoggingModule;
-import org.jclouds.rest.BaseRestClientTest.MockModule;
 import org.jclouds.rest.RequestSigner;
 import org.jclouds.rest.RestContextBuilder;
 import org.jclouds.rest.RestContextFactory;
 import org.jclouds.rest.RestContextSpec;
+import org.jclouds.rest.BaseRestClientTest.MockModule;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -48,8 +48,8 @@
 @Test(groups = "unit", testName = "QuerySignerTest")
 public class QuerySignerTest {
    @SuppressWarnings({ "unchecked", "rawtypes" })
-   public static final RestContextSpec<Map, List> DUMMY_SPEC = new RestContextSpec<Map, List>("cloudstack",
-         "http://localhost:8080/client/api", "2.2", "", "apiKey", "secretKey", Map.class, List.class,
+   public static final RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> DUMMY_SPEC = new RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient>("cloudstack",
+         "http://localhost:8080/client/api", "2.2", "", "", "apiKey", "secretKey", IntegrationTestClient.class, IntegrationTestAsyncClient.class,
          PropertiesBuilder.class, (Class) RestContextBuilder.class, ImmutableList.<Module> of(new MockModule(),
                new NullLoggingModule(), new AbstractModule() {
                   @Override
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListClustersOptionsTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListClustersOptionsTest.java
new file mode 100644
index 0000000..502fa6f
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListClustersOptionsTest.java
@@ -0,0 +1,126 @@
+/**
+ * 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.cloudstack.options;
+
+import com.google.common.collect.ImmutableList;
+import org.jclouds.cloudstack.domain.Cluster;
+import org.jclouds.cloudstack.domain.Host;
+import org.testng.annotations.Test;
+
+import static org.jclouds.cloudstack.options.ListClustersOptions.Builder.*;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Tests behavior of {@code ListClustersOptions}
+ *
+ * @author Richard Downer
+ */
+@Test(groups = "unit")
+public class ListClustersOptionsTest {
+
+   public void testAllocationState() {
+      ListClustersOptions options = new ListClustersOptions().allocationState(Host.AllocationState.ENABLED);
+      assertEquals(ImmutableList.of("Enabled"), options.buildQueryParameters().get("allocationstate"));
+   }
+
+   public void testAllocationStateStatic() {
+      ListClustersOptions options = allocationState(Host.AllocationState.ENABLED);
+      assertEquals(ImmutableList.of("Enabled"), options.buildQueryParameters().get("allocationstate"));
+   }
+
+   public void testClusterType() {
+      ListClustersOptions options = new ListClustersOptions().clusterType(Host.ClusterType.CLOUD_MANAGED);
+      assertEquals(ImmutableList.of("CloudManaged"), options.buildQueryParameters().get("clustertype"));
+   }
+
+   public void testClusterTypeStatic() {
+      ListClustersOptions options = clusterType(Host.ClusterType.CLOUD_MANAGED);
+      assertEquals(ImmutableList.of("CloudManaged"), options.buildQueryParameters().get("clustertype"));
+   }
+
+   public void testHypervisor() {
+      ListClustersOptions options = new ListClustersOptions().hypervisor("XenServer");
+      assertEquals(ImmutableList.of("XenServer"), options.buildQueryParameters().get("hypervisor"));
+   }
+
+   public void testHypervisorStatic() {
+      ListClustersOptions options = hypervisor("XenServer");
+      assertEquals(ImmutableList.of("XenServer"), options.buildQueryParameters().get("hypervisor"));
+   }
+
+   public void testId() {
+      ListClustersOptions options = new ListClustersOptions().id(42L);
+      assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("id"));
+   }
+
+   public void testIdStatic() {
+      ListClustersOptions options = id(42L);
+      assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("id"));
+   }
+
+   public void testKeyword() {
+      ListClustersOptions options = new ListClustersOptions().keyword("Enabled");
+      assertEquals(ImmutableList.of("Enabled"), options.buildQueryParameters().get("keyword"));
+   }
+
+   public void testKeywordStatic() {
+      ListClustersOptions options = keyword("Enabled");
+      assertEquals(ImmutableList.of("Enabled"), options.buildQueryParameters().get("keyword"));
+   }
+
+   public void testManagedState() {
+      ListClustersOptions options = new ListClustersOptions().managedState(Cluster.ManagedState.PREPARE_UNMANAGED);
+      assertEquals(ImmutableList.of("PrepareUnmanaged"), options.buildQueryParameters().get("managedstate"));
+   }
+
+   public void testManagedStateStatic() {
+      ListClustersOptions options = managedState(Cluster.ManagedState.PREPARE_UNMANAGED);
+      assertEquals(ImmutableList.of("PrepareUnmanaged"), options.buildQueryParameters().get("managedstate"));
+   }
+
+   public void testName() {
+      ListClustersOptions options = new ListClustersOptions().name("Host Name");
+      assertEquals(ImmutableList.of("Host Name"), options.buildQueryParameters().get("name"));
+   }
+
+   public void testNameStatic() {
+      ListClustersOptions options = name("Host Name");
+      assertEquals(ImmutableList.of("Host Name"), options.buildQueryParameters().get("name"));
+   }
+
+   public void testPodId() {
+      ListClustersOptions options = new ListClustersOptions().podId(42L);
+      assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("podid"));
+   }
+
+   public void testPodIdStatic() {
+      ListClustersOptions options = podId(42L);
+      assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("podid"));
+   }
+
+   public void testZoneId() {
+      ListClustersOptions options = new ListClustersOptions().zoneId(42L);
+      assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("zoneid"));
+   }
+
+   public void testZoneIdStatic() {
+      ListClustersOptions options = zoneId(42L);
+      assertEquals(ImmutableList.of("42"), options.buildQueryParameters().get("zoneid"));
+   }
+}
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListLoadBalancerRulesOptionsTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListLoadBalancerRulesOptionsTest.java
index 7f0f1b4..51ee772 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListLoadBalancerRulesOptionsTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListLoadBalancerRulesOptionsTest.java
@@ -22,8 +22,11 @@
 import static org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions.Builder.domainId;
 import static org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions.Builder.id;
 import static org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions.Builder.name;
+import static org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions.Builder.page;
+import static org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions.Builder.pageSize;
 import static org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions.Builder.publicIPId;
 import static org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions.Builder.virtualMachineId;
+import static org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions.Builder.zoneId;
 import static org.testng.Assert.assertEquals;
 
 import org.testng.annotations.Test;
@@ -32,7 +35,7 @@
 
 /**
  * Tests behavior of {@code ListLoadBalancerRulesOptions}
- * 
+ *
  * @author Adrian Cole
  */
 @Test(groups = "unit")
@@ -99,4 +102,34 @@
       ListLoadBalancerRulesOptions options = virtualMachineId(6);
       assertEquals(ImmutableList.of("6"), options.buildQueryParameters().get("virtualmachineid"));
    }
+
+   public void testZoneId() {
+      ListLoadBalancerRulesOptions options = new ListLoadBalancerRulesOptions().zoneId(6);
+      assertEquals(ImmutableList.of("6"), options.buildQueryParameters().get("zoneid"));
+   }
+
+   public void testZoneIdStatic() {
+      ListLoadBalancerRulesOptions options = zoneId(6);
+      assertEquals(ImmutableList.of("6"), options.buildQueryParameters().get("zoneid"));
+   }
+
+   public void testPage() {
+      ListLoadBalancerRulesOptions options = new ListLoadBalancerRulesOptions().page(6);
+      assertEquals(ImmutableList.of("6"), options.buildQueryParameters().get("page"));
+   }
+
+   public void testPageStatic() {
+      ListLoadBalancerRulesOptions options = page(6);
+      assertEquals(ImmutableList.of("6"), options.buildQueryParameters().get("page"));
+   }
+
+   public void testPageSize() {
+      ListLoadBalancerRulesOptions options = new ListLoadBalancerRulesOptions().pageSize(6);
+      assertEquals(ImmutableList.of("6"), options.buildQueryParameters().get("pagesize"));
+   }
+
+   public void testPageSizeStatic() {
+      ListLoadBalancerRulesOptions options = pageSize(6);
+      assertEquals(ImmutableList.of("6"), options.buildQueryParameters().get("pagesize"));
+   }
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListPortForwardingRulesOptionsTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListPortForwardingRulesOptionsTest.java
index 66a4020..595e8f9 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListPortForwardingRulesOptionsTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/options/ListPortForwardingRulesOptionsTest.java
@@ -18,9 +18,9 @@
  */
 package org.jclouds.cloudstack.options;
 
-import static org.jclouds.cloudstack.options.ListPortForwardingRulesOptions.Builder.IPAddressId;
 import static org.jclouds.cloudstack.options.ListPortForwardingRulesOptions.Builder.accountInDomain;
 import static org.jclouds.cloudstack.options.ListPortForwardingRulesOptions.Builder.domainId;
+import static org.jclouds.cloudstack.options.ListPortForwardingRulesOptions.Builder.ipAddressId;
 import static org.testng.Assert.assertEquals;
 
 import org.testng.annotations.Test;
@@ -48,12 +48,12 @@
    }
 
    public void testName() {
-      ListPortForwardingRulesOptions options = new ListPortForwardingRulesOptions().IPAddressId(9);
+      ListPortForwardingRulesOptions options = new ListPortForwardingRulesOptions().ipAddressId(9);
       assertEquals(ImmutableList.of("9"), options.buildQueryParameters().get("ipaddressid"));
    }
 
    public void testNameStatic() {
-      ListPortForwardingRulesOptions options = IPAddressId(9);
+      ListPortForwardingRulesOptions options = ipAddressId(9);
       assertEquals(ImmutableList.of("9"), options.buildQueryParameters().get("ipaddressid"));
    }
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListClustersResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListClustersResponseTest.java
new file mode 100644
index 0000000..8f6354b
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListClustersResponseTest.java
@@ -0,0 +1,73 @@
+/**
+ * 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.cloudstack.parse;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.jclouds.cloudstack.config.CloudStackParserModule;
+import org.jclouds.cloudstack.domain.Cluster;
+import org.jclouds.cloudstack.domain.Host;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import java.util.Set;
+
+/**
+ * @author Richard Downer
+ */
+@Test(groups = "unit")
+public class ListClustersResponseTest extends BaseSetParserTest<Cluster> {
+
+   @Override
+   public String resource() {
+      return "/listclustersresponse.json";
+   }
+
+   @Override
+   @SelectJson("cluster")
+   public Set<Cluster> expected() {
+      Cluster cluster1 = Cluster.builder()
+         .id(1)
+         .name("Xen Clust 1")
+         .podId(1).podName("Dev Pod 1")
+         .zoneId(1).zoneName("Dev Zone 1")
+         .hypervisor("XenServer")
+         .clusterType(Host.ClusterType.CLOUD_MANAGED)
+         .allocationState(Host.AllocationState.ENABLED)
+         .managedState(Cluster.ManagedState.MANAGED)
+         .build();
+      Cluster cluster2 = Cluster.builder()
+         .id(2)
+         .name("Xen Clust 1")
+         .podId(2).podName("Dev Pod 2")
+         .zoneId(2).zoneName("Dev Zone 2")
+         .hypervisor("XenServer")
+         .clusterType(Host.ClusterType.CLOUD_MANAGED)
+         .allocationState(Host.AllocationState.ENABLED)
+         .managedState(Cluster.ManagedState.MANAGED)
+         .build();
+
+      return ImmutableSet.of(cluster1, cluster2);
+   }
+
+}
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListConfigurationEntriesResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListConfigurationEntriesResponseTest.java
new file mode 100644
index 0000000..0a734a9
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListConfigurationEntriesResponseTest.java
@@ -0,0 +1,69 @@
+/**
+ * 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.cloudstack.parse;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.jclouds.cloudstack.config.CloudStackParserModule;
+import org.jclouds.cloudstack.domain.ConfigurationEntry;
+import org.jclouds.cloudstack.domain.FirewallRule;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import java.util.Set;
+
+/**
+ * @author Andrei Savu
+ */
+@Test(groups = "unit")
+public class ListConfigurationEntriesResponseTest extends BaseSetParserTest<ConfigurationEntry> {
+
+   @Override
+   protected Injector injector() {
+      return Guice.createInjector(new CloudStackParserModule(), new GsonModule() {
+
+         @Override
+         protected void configure() {
+            bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+            super.configure();
+         }
+
+      });
+   }
+
+   @Override
+   public String resource() {
+      return "/listconfigurationsresponse.json";
+   }
+
+   @Override
+   @SelectJson("configuration")
+   public Set<ConfigurationEntry> expected() {
+      return ImmutableSet.of(
+         ConfigurationEntry.builder().category("Advanced").name("account.cleanup.interval").value("86400")
+            .description("The interval (in seconds) between cleanup for removed accounts").build(),
+         ConfigurationEntry.builder().category("Advanced").name("agent.lb.enabled").value("true")
+            .description("If agent load balancing enabled in cluster setup").build()
+      );
+   }
+
+}
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListFirewallRulesResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListFirewallRulesResponseTest.java
new file mode 100644
index 0000000..1a67ca3
--- /dev/null
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListFirewallRulesResponseTest.java
@@ -0,0 +1,72 @@
+/**
+ * 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.cloudstack.parse;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.jclouds.cloudstack.config.CloudStackParserModule;
+import org.jclouds.cloudstack.domain.FirewallRule;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import java.util.Set;
+
+/**
+ * 
+ * @author Andrei Savu
+ */
+@Test(groups = "unit")
+public class ListFirewallRulesResponseTest extends BaseSetParserTest<FirewallRule> {
+
+   @Override
+   protected Injector injector() {
+      return Guice.createInjector(new CloudStackParserModule(), new GsonModule() {
+
+         @Override
+         protected void configure() {
+            bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+            super.configure();
+         }
+
+      });
+   }
+
+   @Override
+   public String resource() {
+      return "/listfirewallrulesresponse.json";
+   }
+
+   @Override
+   @SelectJson("firewallrule")
+   public Set<FirewallRule> expected() {
+      Set<String> CIDRs = ImmutableSet.of("0.0.0.0/0");
+      return ImmutableSet.of(
+         FirewallRule.builder().id(2017).protocol(FirewallRule.Protocol.TCP).startPort(30)
+            .endPort(35).ipAddressId(2).ipAddress("10.27.27.51").state(FirewallRule.State.ACTIVE).CIDRs(CIDRs).build(),
+         FirewallRule.builder().id(2016).protocol(FirewallRule.Protocol.TCP).startPort(22)
+            .endPort(22).ipAddressId(2).ipAddress("10.27.27.51").state(FirewallRule.State.ACTIVE).CIDRs(CIDRs).build(),
+         FirewallRule.builder().id(10).protocol(FirewallRule.Protocol.TCP).startPort(22)
+            .endPort(22).ipAddressId(8).ipAddress("10.27.27.57").state(FirewallRule.State.ACTIVE).CIDRs(CIDRs).build()
+      );
+   }
+
+}
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListPortForwardingRulesResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListPortForwardingRulesResponseTest.java
index 85b41e6..5079c7c 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListPortForwardingRulesResponseTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListPortForwardingRulesResponseTest.java
@@ -61,10 +61,12 @@
    public Set<PortForwardingRule> expected() {
       Set<String> cidrs = ImmutableSet.of("0.0.0.0/1", "128.0.0.0/1");
       return ImmutableSet.<PortForwardingRule> of(
-         PortForwardingRule.builder().id(15).privatePort(22).protocol("tcp").publicPort(2022).virtualMachineId(3)
-            .virtualMachineName("i-3-3-VM").IPAddressId(3).IPAddress("72.52.126.32").state("Active").CIDRs(cidrs).build(),
-         PortForwardingRule.builder().id(18).privatePort(22).protocol("tcp").publicPort(22).virtualMachineId(89)
-            .virtualMachineName("i-3-89-VM").IPAddressId(34).IPAddress("72.52.126.63").state("Active").build());
+         PortForwardingRule.builder().id(15).privatePort(22).protocol(PortForwardingRule.Protocol.TCP)
+            .publicPort(2022).virtualMachineId(3).virtualMachineName("i-3-3-VM").IPAddressId(3)
+            .IPAddress("72.52.126.32").state(PortForwardingRule.State.ACTIVE).CIDRs(cidrs).build(),
+         PortForwardingRule.builder().id(18).privatePort(22).protocol(PortForwardingRule.Protocol.TCP)
+            .publicPort(22).virtualMachineId(89).virtualMachineName("i-3-89-VM").IPAddressId(34)
+            .IPAddress("72.52.126.63").state(PortForwardingRule.State.ACTIVE).build());
    }
 
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/NetworkPredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/NetworkPredicatesTest.java
index effd125..9ff6b8f 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/NetworkPredicatesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/NetworkPredicatesTest.java
@@ -18,10 +18,7 @@
  */
 package org.jclouds.cloudstack.predicates;
 
-import static org.jclouds.cloudstack.predicates.NetworkPredicates.hasLoadBalancerService;
-import static org.jclouds.cloudstack.predicates.NetworkPredicates.supportsPortForwarding;
-import static org.jclouds.cloudstack.predicates.NetworkPredicates.supportsStaticNAT;
-
+import com.google.common.base.Predicate;
 import org.jclouds.cloudstack.domain.Network;
 import org.jclouds.cloudstack.domain.NetworkService;
 import org.testng.annotations.Test;
@@ -30,6 +27,10 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
+import static org.jclouds.cloudstack.predicates.NetworkPredicates.*;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
 /**
  * 
  * @author Adrian Cole
@@ -93,4 +94,17 @@
       assert !hasLoadBalancerService().apply(network);
 
    }
+
+   public void testDefaultNetworkInZone() {
+      Network defaultInZone = Network.builder().isDefault(true).zoneId(42).build();
+      Network defaultNotInZone = Network.builder().isDefault(true).zoneId(200).build();
+      Network notDefaultInZone = Network.builder().isDefault(false).zoneId(42).build();
+      Network notDefaultNotInZone = Network.builder().isDefault(false).zoneId(200).build();
+
+      Predicate<Network> predicate = defaultNetworkInZone(42);
+      assertTrue(predicate.apply(defaultInZone));
+      assertFalse(predicate.apply(defaultNotInZone));
+      assertFalse(predicate.apply(notDefaultInZone));
+      assertFalse(predicate.apply(notDefaultNotInZone));
+   }
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/TemplatePredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/TemplatePredicatesTest.java
index a98aedb..7f92319 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/TemplatePredicatesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/TemplatePredicatesTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.cloudstack.predicates;
 
 import org.jclouds.cloudstack.domain.Template;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/UserPredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/UserPredicatesTest.java
index 4fe137a..74b8e68 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/UserPredicatesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/UserPredicatesTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.cloudstack.predicates;
 
 import org.jclouds.cloudstack.domain.Account;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/ZonePredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/ZonePredicatesTest.java
index 0486521..561fce0 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/ZonePredicatesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/ZonePredicatesTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.cloudstack.predicates;
 
 import org.jclouds.cloudstack.domain.NetworkType;
diff --git a/apis/cloudstack/src/test/resources/createfirewallrulesresponse.json b/apis/cloudstack/src/test/resources/createfirewallrulesresponse.json
new file mode 100644
index 0000000..f6a727b
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/createfirewallrulesresponse.json
@@ -0,0 +1 @@
+{ "createfirewallruleresponse" : {"jobid":2036,"id":2017} }
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/createportforwardingrulesresponse.json b/apis/cloudstack/src/test/resources/createportforwardingrulesresponse.json
new file mode 100644
index 0000000..cf29e1c
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/createportforwardingrulesresponse.json
@@ -0,0 +1 @@
+{ "createportforwardingruleresponse" : {"jobid":2035,"id":2015} }
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/deletefirewallrulesresponse.json b/apis/cloudstack/src/test/resources/deletefirewallrulesresponse.json
new file mode 100644
index 0000000..abf5b6d
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/deletefirewallrulesresponse.json
@@ -0,0 +1 @@
+{ "deletefirewallruleresponse" : {"jobid":2037} }
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/deleteportforwardingrulesresponse.json b/apis/cloudstack/src/test/resources/deleteportforwardingrulesresponse.json
new file mode 100644
index 0000000..552ad94
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/deleteportforwardingrulesresponse.json
@@ -0,0 +1 @@
+{ "deleteportforwardingruleresponse" : {"jobid":2038} }
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/getfirewallrulesresponse.json b/apis/cloudstack/src/test/resources/getfirewallrulesresponse.json
new file mode 100644
index 0000000..88d3d47
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/getfirewallrulesresponse.json
@@ -0,0 +1,2 @@
+{ "listfirewallrulesresponse" : { "count":1 ,"firewallrule" : [
+    {"id":2017,"protocol":"tcp","startport":"30","endport":"35","ipaddressid":2,"ipaddress":"10.27.27.51","state":"Active","cidrlist":"0.0.0.0/0"} ] } }
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/getportforwardingrulesresponse.json b/apis/cloudstack/src/test/resources/getportforwardingrulesresponse.json
new file mode 100644
index 0000000..3ea044d
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/getportforwardingrulesresponse.json
@@ -0,0 +1,2 @@
+{ "listportforwardingrulesresponse" : { "portforwardingrule" : [
+    {"id":15,"privateport":"22","protocol":"tcp","publicport":"2022","virtualmachineid":3,"virtualmachinename":"i-3-3-VM","ipaddressid":3,"ipaddress":"72.52.126.32","state":"Active","cidrlist":"0.0.0.0/1,128.0.0.0/1"} ] } }
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/listclustersresponse.json b/apis/cloudstack/src/test/resources/listclustersresponse.json
new file mode 100644
index 0000000..64dd052
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/listclustersresponse.json
@@ -0,0 +1 @@
+{ "listclustersresponse" : { "count":2 ,"cluster" : [  {"id":1,"name":"Xen Clust 1","podid":1,"podname":"Dev Pod 1","zoneid":1,"zonename":"Dev Zone 1","hypervisortype":"XenServer","clustertype":"CloudManaged","allocationstate":"Enabled","managedstate":"Managed"}, {"id":2,"name":"Xen Clust 1","podid":2,"podname":"Dev Pod 2","zoneid":2,"zonename":"Dev Zone 2","hypervisortype":"XenServer","clustertype":"CloudManaged","allocationstate":"Enabled","managedstate":"Managed"} ] } }
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/listconfigurationsresponse.json b/apis/cloudstack/src/test/resources/listconfigurationsresponse.json
new file mode 100644
index 0000000..25bc7a1
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/listconfigurationsresponse.json
@@ -0,0 +1,4 @@
+{ "listconfigurationsresponse" : { "count":2 ,"configuration" : [
+    {"category":"Advanced","name":"account.cleanup.interval","value":"86400","description":"The interval (in seconds) between cleanup for removed accounts"},
+    {"category":"Advanced","name":"agent.lb.enabled","value":"true","description":"If agent load balancing enabled in cluster setup"}]
+}}
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/listfirewallrulesresponse.json b/apis/cloudstack/src/test/resources/listfirewallrulesresponse.json
new file mode 100644
index 0000000..e93d76e
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/listfirewallrulesresponse.json
@@ -0,0 +1,4 @@
+{ "listfirewallrulesresponse" : { "count":3 ,"firewallrule" : [
+    {"id":2017,"protocol":"tcp","startport":"30","endport":"35","ipaddressid":2,"ipaddress":"10.27.27.51","state":"Active","cidrlist":"0.0.0.0/0"},
+    {"id":2016,"protocol":"tcp","startport":"22","endport":"22","ipaddressid":2,"ipaddress":"10.27.27.51","state":"Active","cidrlist":"0.0.0.0/0"},
+    {"id":10,"protocol":"tcp","startport":"22","endport":"22","ipaddressid":8,"ipaddress":"10.27.27.57","state":"Active","cidrlist":"0.0.0.0/0"} ] } }
\ No newline at end of file
diff --git a/apis/cloudstack/src/test/resources/updateconfigurationsresponse.json b/apis/cloudstack/src/test/resources/updateconfigurationsresponse.json
new file mode 100644
index 0000000..6013bd5
--- /dev/null
+++ b/apis/cloudstack/src/test/resources/updateconfigurationsresponse.json
@@ -0,0 +1,2 @@
+{ "updateconfigurationresponse" :  { "configuration" :
+    {"category":"Advanced","name":"expunge.delay","value":"11","description":"Determines how long (in seconds) to wait before actually expunging destroyed vm. The default value = the default value of expunge.interval"} }  }
\ No newline at end of file
diff --git a/apis/cloudwatch/pom.xml b/apis/cloudwatch/pom.xml
index bf0b2aa..559236a 100644
--- a/apis/cloudwatch/pom.xml
+++ b/apis/cloudwatch/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.cloudwatch.endpoint>https://monitoring.us-east-1.amazonaws.com</test.cloudwatch.endpoint>
-    <test.cloudwatch.apiversion>2009-05-15</test.cloudwatch.apiversion>
+    <test.cloudwatch.api-version>2009-05-15</test.cloudwatch.api-version>
+    <test.cloudwatch.build-version></test.cloudwatch.build-version>
     <test.cloudwatch.identity>${test.aws.identity}</test.cloudwatch.identity>
     <test.cloudwatch.credential>${test.aws.credential}</test.cloudwatch.credential>
   </properties>
@@ -86,7 +87,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.cloudwatch.endpoint>${test.cloudwatch.endpoint}</test.cloudwatch.endpoint>
-                    <test.cloudwatch.apiversion>${test.cloudwatch.apiversion}</test.cloudwatch.apiversion>
+                    <test.cloudwatch.api-version>${test.cloudwatch.api-version}</test.cloudwatch.api-version>
+                    <test.cloudwatch.build-version>${test.cloudwatch.build-version}</test.cloudwatch.build-version>
                     <test.cloudwatch.identity>${test.cloudwatch.identity}</test.cloudwatch.identity>
                     <test.cloudwatch.credential>${test.cloudwatch.credential}</test.cloudwatch.credential>
                   </systemPropertyVariables>
diff --git a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/domain/Statistics.java b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/domain/Statistics.java
index 6405a05..b44b5f3 100644
--- a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/domain/Statistics.java
+++ b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/domain/Statistics.java
@@ -1,3 +1,21 @@
+/**
+ * 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.cloudwatch.domain;
 
 import com.google.common.base.CaseFormat;
diff --git a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchClientLiveTest.java b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchClientLiveTest.java
index 5b27763..672eaea 100644
--- a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchClientLiveTest.java
+++ b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchClientLiveTest.java
@@ -25,12 +25,12 @@
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
 import org.jclouds.cloudwatch.domain.Datapoint;
 import org.jclouds.cloudwatch.domain.Statistics;
 import org.jclouds.cloudwatch.domain.Unit;
 import org.jclouds.cloudwatch.options.GetMetricStatisticsOptions;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
+import org.jclouds.rest.BaseRestClientLiveTest;
 import org.jclouds.rest.RestContext;
 import org.jclouds.rest.RestContextFactory;
 import org.testng.annotations.AfterTest;
@@ -45,37 +45,15 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class CloudWatchClientLiveTest {
+@Test(groups = "live", singleThreaded = true)
+public class CloudWatchClientLiveTest extends BaseRestClientLiveTest {
+   public CloudWatchClientLiveTest() {
+      provider = "cloudwatch";
+   }
 
    private CloudWatchClient client;
    private RestContext<CloudWatchClient, CloudWatchAsyncClient> context;
-   protected String provider = "cloudwatch";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
 
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint", null);
-      apiversion = System.getProperty("test." + provider + ".apiversion", null);
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/apis/deltacloud/pom.xml b/apis/deltacloud/pom.xml
index 6b52075..7b8e438 100644
--- a/apis/deltacloud/pom.xml
+++ b/apis/deltacloud/pom.xml
@@ -49,7 +49,8 @@
   
   <properties>
     <test.deltacloud.endpoint>http://localhost:3001/api</test.deltacloud.endpoint>
-    <test.deltacloud.apiversion>0.3.0</test.deltacloud.apiversion>
+    <test.deltacloud.api-version>0.3.0</test.deltacloud.api-version>
+    <test.deltacloud.build-version></test.deltacloud.build-version>
     <test.deltacloud.identity>mockuser</test.deltacloud.identity>
     <test.deltacloud.credential>mockpassword</test.deltacloud.credential>
     <test.deltacloud.image-id />
@@ -107,7 +108,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.deltacloud.endpoint>${test.deltacloud.endpoint}</test.deltacloud.endpoint>
-                    <test.deltacloud.apiversion>${test.deltacloud.apiversion}</test.deltacloud.apiversion>
+                    <test.deltacloud.api-version>${test.deltacloud.api-version}</test.deltacloud.api-version>
+                    <test.deltacloud.build-version>${test.deltacloud.build-version}</test.deltacloud.build-version>
                     <test.deltacloud.identity>${test.deltacloud.identity}</test.deltacloud.identity>
                     <test.deltacloud.credential>${test.deltacloud.credential}</test.deltacloud.credential>
                     <test.deltacloud.image-id>${test.deltacloud.image-id}</test.deltacloud.image-id>
diff --git a/apis/deltacloud/src/test/java/org/jclouds/deltacloud/ReadOnlyDeltacloudClientLiveTest.java b/apis/deltacloud/src/test/java/org/jclouds/deltacloud/ReadOnlyDeltacloudClientLiveTest.java
index 30061ef..10ed812 100644
--- a/apis/deltacloud/src/test/java/org/jclouds/deltacloud/ReadOnlyDeltacloudClientLiveTest.java
+++ b/apis/deltacloud/src/test/java/org/jclouds/deltacloud/ReadOnlyDeltacloudClientLiveTest.java
@@ -26,7 +26,7 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.deltacloud.domain.DeltacloudCollection;
 import org.jclouds.deltacloud.domain.HardwareProfile;
@@ -59,40 +59,19 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true, testName = "ReadOnlyDeltacloudClientLiveTest")
-public class ReadOnlyDeltacloudClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "ReadOnlyDeltacloudClientLiveTest")
+public class ReadOnlyDeltacloudClientLiveTest extends BaseVersionedServiceLiveTest {
+   public ReadOnlyDeltacloudClientLiveTest() {
+      provider = "deltacloud";
+   }
 
    protected DeltacloudClient client;
    protected RestContext<DeltacloudClient, DeltacloudAsyncClient> context;
 
-   protected String provider = "deltacloud";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
+
    protected Predicate<IPSocket> socketTester;
    protected ImmutableMap<State, Predicate<Instance>> stateChanges;
 
-   protected void setupCredentials() {
-      identity = System.getProperty("test." + provider + ".identity", "mockuser");
-      credential = System.getProperty("test." + provider + ".credential", "mockpassword");
-      endpoint = System.getProperty("test." + provider + ".endpoint", "http://localhost:3001/api");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = "live")
    public void setupClient() {
diff --git a/apis/ec2/pom.xml b/apis/ec2/pom.xml
index 7b0823a..4132100 100644
--- a/apis/ec2/pom.xml
+++ b/apis/ec2/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.ec2.endpoint>https://ec2.us-east-1.amazonaws.com</test.ec2.endpoint>
-    <test.ec2.apiversion>2010-06-15</test.ec2.apiversion>
+    <test.ec2.api-version>2010-06-15</test.ec2.api-version>
+    <test.ec2.build-version></test.ec2.build-version>
     <test.ec2.identity>${test.aws.identity}</test.ec2.identity>
     <test.ec2.credential>${test.aws.credential}</test.ec2.credential>
     <test.ec2.image-id />
@@ -102,7 +103,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.ec2.endpoint>${test.ec2.endpoint}</test.ec2.endpoint>
-                    <test.ec2.apiversion>${test.ec2.apiversion}</test.ec2.apiversion>
+                    <test.ec2.api-version>${test.ec2.api-version}</test.ec2.api-version>
+                    <test.ec2.build-version>${test.ec2.build-version}</test.ec2.build-version>
                     <test.ec2.identity>${test.ec2.identity}</test.ec2.identity>
                     <test.ec2.credential>${test.ec2.credential}</test.ec2.credential>
                     <test.ec2.image-id>${test.ec2.image-id}</test.ec2.image-id>
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
index 463f56e..a974767 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
@@ -56,8 +56,6 @@
 import org.jclouds.ec2.domain.RunningInstance;
 import org.jclouds.ec2.reference.EC2Constants;
 import org.jclouds.predicates.RetryablePredicate;
-import org.jclouds.rest.RestContext;
-import org.jclouds.rest.internal.RestContextImpl;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
@@ -112,9 +110,6 @@
       bind(new TypeLiteral<ComputeServiceContext>() {
       }).to(new TypeLiteral<ComputeServiceContextImpl<EC2Client, EC2AsyncClient>>() {
       }).in(Scopes.SINGLETON);
-      bind(new TypeLiteral<RestContext<EC2Client, EC2AsyncClient>>() {
-      }).to(new TypeLiteral<RestContextImpl<EC2Client, EC2AsyncClient>>() {
-      }).in(Scopes.SINGLETON);
    }
 
    /**
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2ListNodesStrategy.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2ListNodesStrategy.java
index 6e67abc..88842af 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2ListNodesStrategy.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/EC2ListNodesStrategy.java
@@ -96,6 +96,7 @@
                   @SuppressWarnings("unchecked")
                   @Override
                   public Future<Set<? extends Reservation<? extends RunningInstance>>> apply(String from) {
+                     // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7126754
                      return castToSpecificTypedFuture(client.getInstanceServices().describeInstancesInRegion(from));
                   }
 
@@ -104,7 +105,6 @@
       return concat(concat(reservations));
    }
 
-   // "hide" this cast (i.e. do not perform inline) from the Java 7 compiler - see http://stackoverflow.com/questions/8637937/why-does-a-generic-cast-of-a-list-extends-set-to-listset-succeed-on-sun
    @SuppressWarnings("unchecked")
    private static <T> Future<T> castToSpecificTypedFuture(Future<? extends T> input) {
        return (Future<T>) input;
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java
index 1d6adc9..7223131 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermission.java
@@ -46,14 +46,6 @@
 
    /**
     * List of security group and user ID pairs.
-    * 
-    * @see #getUserIdGroupPairs
-    */
-   @Deprecated
-   Set<UserIdGroupPair> getGroups();
-
-   /**
-    * List of security group and user ID pairs.
     */
    Multimap<String, String> getUserIdGroupPairs();
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermissionImpl.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermissionImpl.java
index 7de47ee..fe80198 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermissionImpl.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/IpPermissionImpl.java
@@ -20,7 +20,6 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Map.Entry;
 import java.util.Set;
 
 import com.google.common.collect.ImmutableMultimap;
@@ -144,18 +143,6 @@
     * {@inheritDoc}
     */
    @Override
-   @Deprecated
-   public Set<UserIdGroupPair> getGroups() {
-      ImmutableSet.Builder<UserIdGroupPair> groups = ImmutableSet.<UserIdGroupPair> builder();
-      for (Entry<String, String> pair : userIdGroupPairs.entries())
-         groups.add(new UserIdGroupPair(pair.getKey(), pair.getValue()));
-      return groups.build();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
    public Multimap<String, String> getUserIdGroupPairs() {
       return userIdGroupPairs;
    }
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/KeyPair.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/KeyPair.java
index 0038838..ead73cb 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/KeyPair.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/KeyPair.java
@@ -80,7 +80,7 @@
       }
 
       public static Builder fromKeyPair(KeyPair in) {
-         return new Builder().region(in.getRegion()).keyName(in.getKeyName()).sha1OfPrivateKey(in.getKeyFingerprint())
+         return new Builder().region(in.getRegion()).keyName(in.getKeyName()).sha1OfPrivateKey(in.getSha1OfPrivateKey())
                   .keyMaterial(in.getKeyMaterial());
       }
    }
@@ -117,14 +117,6 @@
    }
 
    /**
-    * @see #getSha1OfPrivateKey
-    */
-   @Deprecated
-   public String getKeyFingerprint() {
-      return sha1OfPrivateKey;
-   }
-
-   /**
     * A SHA-1 digest of the DER encoded private key.
     * 
     * @see SshKeys#sha1
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java
index 8243cf2..a007730 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/CloudApplicationArchitecturesEC2ClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.ec2;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.ec2.options.RunInstancesOptions.Builder.asType;
 import static org.jclouds.scriptbuilder.domain.Statements.exec;
 import static org.testng.Assert.assertEquals;
@@ -34,12 +33,11 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.AWSResponseException;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.ec2.domain.BlockDevice;
-import org.jclouds.ec2.domain.Image.EbsBlockDevice;
 import org.jclouds.ec2.domain.InstanceState;
 import org.jclouds.ec2.domain.InstanceType;
 import org.jclouds.ec2.domain.IpProtocol;
@@ -47,6 +45,7 @@
 import org.jclouds.ec2.domain.PublicIpInstanceIdPair;
 import org.jclouds.ec2.domain.Reservation;
 import org.jclouds.ec2.domain.RunningInstance;
+import org.jclouds.ec2.domain.Image.EbsBlockDevice;
 import org.jclouds.ec2.domain.Volume.InstanceInitiatedShutdownBehavior;
 import org.jclouds.ec2.predicates.InstanceHasIpAddress;
 import org.jclouds.ec2.predicates.InstanceStateRunning;
@@ -80,8 +79,11 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", enabled = false, sequential = true)
-public class CloudApplicationArchitecturesEC2ClientLiveTest {
+@Test(groups = "live", enabled = false, singleThreaded = true, testName = "CloudApplicationArchitecturesEC2ClientLiveTest")
+public class CloudApplicationArchitecturesEC2ClientLiveTest extends BaseVersionedServiceLiveTest {
+   public CloudApplicationArchitecturesEC2ClientLiveTest() {
+      provider = "ec2";
+   }
 
    private EC2Client client;
    protected SshClient.Factory sshFactory;
@@ -95,32 +97,6 @@
    private RetryablePredicate<RunningInstance> hasIpTester;
    private RetryablePredicate<RunningInstance> runningTester;
 
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-            + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-            + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java
index 7d27d65..04819b5 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/EBSBootEC2ClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.ec2;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.ec2.options.CreateSnapshotOptions.Builder.withDescription;
 import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.imageIds;
 import static org.jclouds.ec2.options.RegisterImageBackedByEbsOptions.Builder.withKernelId;
@@ -34,15 +33,13 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.AWSResponseException;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.ec2.domain.Attachment;
 import org.jclouds.ec2.domain.BlockDevice;
 import org.jclouds.ec2.domain.Image;
-import org.jclouds.ec2.domain.Image.Architecture;
-import org.jclouds.ec2.domain.Image.ImageType;
 import org.jclouds.ec2.domain.InstanceState;
 import org.jclouds.ec2.domain.InstanceType;
 import org.jclouds.ec2.domain.IpProtocol;
@@ -52,6 +49,8 @@
 import org.jclouds.ec2.domain.RunningInstance;
 import org.jclouds.ec2.domain.Snapshot;
 import org.jclouds.ec2.domain.Volume;
+import org.jclouds.ec2.domain.Image.Architecture;
+import org.jclouds.ec2.domain.Image.ImageType;
 import org.jclouds.ec2.domain.Volume.InstanceInitiatedShutdownBehavior;
 import org.jclouds.ec2.predicates.InstanceStateRunning;
 import org.jclouds.ec2.predicates.InstanceStateStopped;
@@ -95,13 +94,19 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", enabled = false, sequential = true)
-public class EBSBootEC2ClientLiveTest {
+@Test(groups = "live", enabled = false, singleThreaded = true, testName = "EBSBootEC2ClientLiveTest")
+public class EBSBootEC2ClientLiveTest extends BaseVersionedServiceLiveTest {
+   public EBSBootEC2ClientLiveTest() {
+      provider = "ec2";
+   }
+
+   // TODO: parameterize
+   private static final String IMAGE_ID = "ami-7e28ca17";
+
    // don't need a lot of space. 2GB should be more than enough for testing
    private static final int VOLUME_SIZE = 2;
    private static final String SCRIPT_END = "----COMPLETE----";
    private static final String INSTANCE_PREFIX = System.getProperty("user.name") + ".ec2ebs";
-   private static final String IMAGE_ID = "ami-7e28ca17";
 
    private EC2Client client;
    private SshClient.Factory sshFactory;
@@ -124,32 +129,6 @@
    private Attachment attachment;
    private String mkEbsBoot;
 
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-            + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-            + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
index 1c9a977..48cf8c5 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2ComputeServiceLiveTest.java
@@ -18,11 +18,14 @@
  */
 package org.jclouds.ec2.compute;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 
-import com.google.common.base.Splitter;
 import org.jclouds.compute.BaseComputeServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
@@ -50,7 +53,6 @@
 import org.jclouds.ec2.services.InstanceClient;
 import org.jclouds.ec2.services.KeyPairClient;
 import org.jclouds.ec2.services.SecurityGroupClient;
-import org.jclouds.http.HttpResponseException;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.net.IPSocket;
 import org.jclouds.scriptbuilder.domain.Statements;
@@ -66,8 +68,6 @@
 import com.google.common.collect.Sets;
 import com.google.inject.Module;
 
-import static org.testng.Assert.*;
-
 /**
  * 
  * @author Adrian Cole
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderLiveTest.java
index 9e4ed92..6495c88 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/EC2TemplateBuilderLiveTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.ec2.compute;
 
 import static org.jclouds.http.internal.TrackingJavaUrlHttpCommandExecutorService.getJavaArgsForRequestAtIndex;
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/TestCanRecreateGroupLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/TestCanRecreateGroupLiveTest.java
index c6e6eea..1e80331 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/TestCanRecreateGroupLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/TestCanRecreateGroupLiveTest.java
@@ -18,13 +18,11 @@
  */
 package org.jclouds.ec2.compute;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Properties;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.compute.RunNodesException;
@@ -32,7 +30,6 @@
 import org.jclouds.compute.predicates.NodePredicates;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.sshj.config.SshjSshClientModule;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
@@ -44,37 +41,13 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live")
-public class TestCanRecreateGroupLiveTest {
+@Test(groups = "live", testName="TestCanRecreateGroupLiveTest")
+public class TestCanRecreateGroupLiveTest extends BaseVersionedServiceLiveTest {
+   public TestCanRecreateGroupLiveTest() {
+      provider = "ec2";
+   }
 
    private ComputeServiceContext context;
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() throws FileNotFoundException, IOException {
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionClientLiveTest.java
index 329a678..8c7eb90 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.ec2.options.DescribeAvailabilityZonesOptions.Builder.availabilityZones;
 import static org.jclouds.ec2.options.DescribeRegionsOptions.Builder.regions;
 import static org.testng.Assert.assertEquals;
@@ -26,13 +25,13 @@
 
 import java.net.URI;
 import java.util.Iterator;
-import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.Set;
 import java.util.SortedMap;
+import java.util.Map.Entry;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.EC2Client;
@@ -40,7 +39,6 @@
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.rest.RestContext;
 import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
@@ -54,38 +52,14 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class AvailabilityZoneAndRegionClientLiveTest {
-
+@Test(groups = "live", singleThreaded = true, testName = "AvailabilityZoneAndRegionClientLiveTest")
+public class AvailabilityZoneAndRegionClientLiveTest extends BaseVersionedServiceLiveTest {
+   public AvailabilityZoneAndRegionClientLiveTest() {
+      provider = "ec2";
+   }
+   
    private AvailabilityZoneAndRegionClient client;
    private RestContext<EC2Client, EC2AsyncClient> context;
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticBlockStoreClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticBlockStoreClientLiveTest.java
index 79bc671..4655c5f 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticBlockStoreClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticBlockStoreClientLiveTest.java
@@ -30,6 +30,7 @@
 
 import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.EC2Client;
@@ -57,39 +58,16 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class ElasticBlockStoreClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "ElasticBlockStoreClientLiveTest")
+public class ElasticBlockStoreClientLiveTest extends BaseVersionedServiceLiveTest {
+   public ElasticBlockStoreClientLiveTest() {
+      provider = "ec2";
+   }
+   
    private ElasticBlockStoreClient client;
    private RestContext<EC2Client, EC2AsyncClient> context;
    private String volumeId;
    private Snapshot snapshot;
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticIPAddressClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticIPAddressClientLiveTest.java
index 4391984..d35074b 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticIPAddressClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticIPAddressClientLiveTest.java
@@ -18,15 +18,14 @@
  */
 package org.jclouds.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
 import java.util.Properties;
 import java.util.SortedSet;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.EC2Client;
@@ -47,38 +46,15 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class ElasticIPAddressClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "ElasticIPAddressClientLiveTest")
+public class ElasticIPAddressClientLiveTest extends BaseVersionedServiceLiveTest {
+   public ElasticIPAddressClientLiveTest() {
+      provider = "ec2";
+   }
 
    private ElasticIPAddressClient client;
    private RestContext<EC2Client, EC2AsyncClient> context;
 
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-               + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceClientLiveTest.java
index 4cb4044..9a4d9f0 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceClientLiveTest.java
@@ -18,14 +18,13 @@
  */
 package org.jclouds.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertNotNull;
 
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.EC2Client;
@@ -46,37 +45,15 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class InstanceClientLiveTest {
-   public static final String PREFIX = System.getProperty("user.name") + "-ec2";
+@Test(groups = "live", singleThreaded = true, testName = "InstanceClientLiveTest")
+public class InstanceClientLiveTest extends BaseVersionedServiceLiveTest {
+   public InstanceClientLiveTest() {
+      provider = "ec2";
+   }
 
    private InstanceClient client;
    private RestContext<EC2Client, EC2AsyncClient> context;
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-               + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
+  
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/KeyPairClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/KeyPairClientLiveTest.java
index 3107404..a0d3b86 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/KeyPairClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/KeyPairClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
@@ -26,8 +25,8 @@
 import java.util.Set;
 import java.util.SortedSet;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.EC2Client;
@@ -48,37 +47,15 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class KeyPairClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "KeyPairClientLiveTest")
+public class KeyPairClientLiveTest extends BaseVersionedServiceLiveTest {
+   public KeyPairClientLiveTest() {
+      provider = "ec2";
+   }
 
    private KeyPairClient client;
    private RestContext<EC2Client, EC2AsyncClient> context;
 
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-               + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java
index 9372710..5ff0d10 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
@@ -27,8 +26,8 @@
 import java.util.Set;
 import java.util.SortedSet;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.EC2Client;
@@ -55,39 +54,15 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", singleThreaded = true)
-public class SecurityGroupClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "SecurityGroupClientLiveTest")
+public class SecurityGroupClientLiveTest extends BaseVersionedServiceLiveTest {
+   public SecurityGroupClientLiveTest() {
+      provider = "ec2";
+   }
 
    protected SecurityGroupClient client;
    private RestContext<EC2Client, EC2AsyncClient> context;
 
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-            + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java
index 67ec283..a11244e 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsClientLiveTest.java
@@ -18,14 +18,12 @@
  */
 package org.jclouds.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.util.Properties;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
+import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.EC2Client;
-import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.rest.RestContext;
 import org.testng.annotations.BeforeGroups;
@@ -39,8 +37,11 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class WindowsClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "WindowsClientLiveTest")
+public class WindowsClientLiveTest extends BaseVersionedServiceLiveTest {
+   public WindowsClientLiveTest() {
+      provider = "ec2";
+   }
 
    private WindowsClient client;
    private static final String DEFAULT_INSTANCE = "i-TODO";
@@ -48,31 +49,6 @@
 
    private RestContext<EC2Client, EC2AsyncClient> context;
 
-   protected String provider = "ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-               + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/apis/elasticstack/pom.xml b/apis/elasticstack/pom.xml
index fe9d113..c51c596 100644
--- a/apis/elasticstack/pom.xml
+++ b/apis/elasticstack/pom.xml
@@ -49,12 +49,13 @@
   
   <properties>
     <test.elasticstack.endpoint>https://api.lon-p.elastichosts.com</test.elasticstack.endpoint>
-    <test.elasticstack.apiversion>1.0</test.elasticstack.apiversion>
+    <test.elasticstack.api-version>1.0</test.elasticstack.api-version>
+    <test.elasticstack.build-version></test.elasticstack.build-version>
     <test.elasticstack.identity>FIXME</test.elasticstack.identity>
     <test.elasticstack.credential>FIXME</test.elasticstack.credential>
-    <test.elasticstack.image-id />
-    <test.elasticstack.image.login-user />
-    <test.elasticstack.image.authenticate-sudo />
+    <test.elasticstack.image-id>38df0986-4d85-4b76-b502-3878ffc80161</test.elasticstack.image-id>
+    <test.elasticstack.image.login-user></test.elasticstack.image.login-user>
+    <test.elasticstack.image.authenticate-sudo></test.elasticstack.image.authenticate-sudo>
   </properties>
   <dependencies>
     <dependency>
@@ -107,7 +108,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.elasticstack.endpoint>${test.elasticstack.endpoint}</test.elasticstack.endpoint>
-                    <test.elasticstack.apiversion>${test.elasticstack.apiversion}</test.elasticstack.apiversion>
+                    <test.elasticstack.api-version>${test.elasticstack.api-version}</test.elasticstack.api-version>
+                    <test.elasticstack.build-version>${test.elasticstack.build-version}</test.elasticstack.build-version>
                     <test.elasticstack.identity>${test.elasticstack.identity}</test.elasticstack.identity>
                     <test.elasticstack.credential>${test.elasticstack.credential}</test.elasticstack.credential>
                     <test.elasticstack.image-id>${test.elasticstack.image-id}</test.elasticstack.image-id>
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackClientLiveTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackClientLiveTest.java
index d7bc885..43f35ae 100644
--- a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackClientLiveTest.java
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.elasticstack;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
@@ -28,7 +27,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
+import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.domain.LoginCredentials;
@@ -60,6 +60,7 @@
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.gson.Gson;
@@ -71,57 +72,39 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live")
-public class ElasticStackClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "ElasticStackClientLiveTest")
+public class ElasticStackClientLiveTest extends BaseVersionedServiceLiveTest {
+   public ElasticStackClientLiveTest() {
+      provider = "elasticstack";
+   }
 
    protected long driveSize = 1 * 1024 * 1024 * 1024l;
    protected int maxDriveImageTime = 360;
    protected String vncPassword = "Il0veVNC";
    protected ElasticStackClient client;
+   protected ComputeServiceContext computeContext;
    protected RestContext<ElasticStackClient, ElasticStackAsyncClient> context;
    protected Predicate<IPSocket> socketTester;
-   protected String bootDrive = "38df0986-4d85-4b76-b502-3878ffc80161";
-
-   protected String provider = "elasticstack";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    protected Predicate<DriveInfo> driveNotClaimed;
 
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = "live")
    public void setupClient() {
       setupCredentials();
       Properties overrides = setupProperties();
-      context = new ComputeServiceContextFactory().createContext(provider,
-               ImmutableSet.<Module> of(new Log4JLoggingModule()), overrides).getProviderSpecificContext();
-
+      computeContext = new ComputeServiceContextFactory().createContext(provider,
+               ImmutableSet.<Module> of(new Log4JLoggingModule()), overrides);
+      context = computeContext.getProviderSpecificContext();
+         
       client = context.getApi();
       driveNotClaimed = new RetryablePredicate<DriveInfo>(Predicates.not(new DriveClaimed(client)), maxDriveImageTime,
                1, TimeUnit.SECONDS);
       socketTester = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), maxDriveImageTime, 1,
                TimeUnit.SECONDS);
+      
+      if (Strings.emptyToNull(imageId) == null) {
+         imageId = computeContext.getComputeService().templateBuilder().build().getImage().getId();
+      }
    }
 
    @Test
@@ -377,7 +360,7 @@
 
    protected void prepareDrive() {
       System.err.println("before prepare" + client.getDriveInfo(drive.getUuid()));
-      client.imageDrive(bootDrive, drive.getUuid(), ImageConversionType.GUNZIP);
+      client.imageDrive(imageId, drive.getUuid(), ImageConversionType.GUNZIP);
       assert driveNotClaimed.apply(drive) : client.getDriveInfo(drive.getUuid());
       System.err.println("after prepare" + client.getDriveInfo(drive.getUuid()));
    }
diff --git a/apis/eucalyptus/pom.xml b/apis/eucalyptus/pom.xml
index 7c2f0d7..92dc68f 100644
--- a/apis/eucalyptus/pom.xml
+++ b/apis/eucalyptus/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.eucalyptus.endpoint>http://ecc.eucalyptus.com:8773/services/Eucalyptus</test.eucalyptus.endpoint>
-    <test.eucalyptus.apiversion>2010-06-15</test.eucalyptus.apiversion>
+    <test.eucalyptus.api-version>2010-06-15</test.eucalyptus.api-version>
+    <test.eucalyptus.build-version></test.eucalyptus.build-version>
     <test.eucalyptus.identity>FIXME_IDENTITY</test.eucalyptus.identity>
     <test.eucalyptus.credential>FIXME_CREDENTIAL</test.eucalyptus.credential>
     <test.eucalyptus.image-id />
@@ -102,7 +103,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.eucalyptus.endpoint>${test.eucalyptus.endpoint}</test.eucalyptus.endpoint>
-                    <test.eucalyptus.apiversion>${test.eucalyptus.apiversion}</test.eucalyptus.apiversion>
+                    <test.eucalyptus.api-version>${test.eucalyptus.api-version}</test.eucalyptus.api-version>
+                    <test.eucalyptus.build-version>${test.eucalyptus.build-version}</test.eucalyptus.build-version>
                     <test.eucalyptus.identity>${test.eucalyptus.identity}</test.eucalyptus.identity>
                     <test.eucalyptus.credential>${test.eucalyptus.credential}</test.eucalyptus.credential>
                     <test.eucalyptus.image-id>${test.eucalyptus.image-id}</test.eucalyptus.image-id>
diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemTestInitializer.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemTestInitializer.java
index 9522730..12fbb61 100644
--- a/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemTestInitializer.java
+++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/integration/FilesystemTestInitializer.java
@@ -44,17 +44,18 @@
    }
 
    @Override
-   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiversion,
-            String app, String identity, String credential) throws IOException {
+   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiVersion,
+            String buildVersion, String app, String identity, String credential) throws IOException {
       return new BlobStoreContextFactory().createContext(provider, ImmutableSet.of(configurationModule,
-               new Log4JLoggingModule()), setupProperties(endpoint, apiversion, identity, credential));
+               new Log4JLoggingModule()), setupProperties(endpoint, apiVersion, buildVersion, identity, credential));
    }
 
    @Override
-   protected Properties setupProperties(String endpoint, String apiversion, String identity, String credential) {
-      Properties props = super.setupProperties(endpoint, apiversion, identity, credential);
+   protected Properties setupProperties(String endpoint, String apiVersion, String buildVersion, String identity,
+            String credential) {
+      Properties props = super.setupProperties(endpoint, apiVersion, buildVersion, identity, credential);
       props.setProperty(FilesystemConstants.PROPERTY_BASEDIR, TestUtils.TARGET_BASE_DIR);
       return props;
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/nova/pom.xml b/apis/nova/pom.xml
index 7ad5eb4..e052dec 100644
--- a/apis/nova/pom.xml
+++ b/apis/nova/pom.xml
@@ -35,7 +35,8 @@
 

   <properties>

     <test.nova.endpoint>http://localhost:8773/services/Cloud</test.nova.endpoint>

-    <test.nova.apiversion>1.1</test.nova.apiversion>

+    <test.nova.api-version>1.1</test.nova.api-version>

+    <test.nova.build-version></test.nova.build-version>

     <test.nova.identity>FIXME_IDENTITY</test.nova.identity>

     <test.nova.credential>FIXME_CREDENTIALS</test.nova.credential>

     <test.nova.image-id />

@@ -115,7 +116,8 @@
                 <configuration>

                   <systemPropertyVariables>

                     <test.nova.endpoint>${test.nova.endpoint}</test.nova.endpoint>

-                    <test.nova.apiversion>${test.nova.apiversion}</test.nova.apiversion>

+                    <test.nova.api-version>${test.nova.api-version}</test.nova.api-version>

+                    <test.nova.build-version>${test.nova.build-version}</test.nova.build-version>

                     <test.nova.identity>${test.nova.identity}</test.nova.identity>

                     <test.nova.credential>${test.nova.credential}</test.nova.credential>

                     <test.nova.image-id>${test.nova.image-id}</test.nova.image-id>

diff --git a/apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java
index 03934f4..205eb74 100644
--- a/apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java
+++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/config/NovaRestClientModule.java
@@ -27,6 +27,7 @@
 import org.jclouds.openstack.nova.ServerManagement;
 import org.jclouds.openstack.nova.handlers.ParseNovaErrorFromHttpResponse;
 import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpRetryHandler;
 import org.jclouds.http.RequiresHttp;
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
@@ -35,6 +36,7 @@
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
 import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
 import org.jclouds.openstack.config.OpenStackAuthenticationModule;
+import org.jclouds.openstack.handlers.RetryOnRenew;
 import org.jclouds.openstack.reference.AuthHeaders;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.config.RestClientModule;
@@ -74,6 +76,11 @@
       bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseNovaErrorFromHttpResponse.class);
    }
 
+   @Override
+   protected void bindRetryHandlers() {
+      bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class);
+   }
+   
    @Provides
    @Singleton
    @ServerManagement
diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java
index f952646..47d8444 100644
--- a/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java
+++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaAsyncClientTest.java
@@ -78,7 +78,7 @@
             createServerOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method, "ralphie", 2, 1);
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, "{\"server\":{\"name\":\"ralphie\",\"imageRef\":\"2\",\"flavorRef\":\"1\"}}",
             "application/json", false);
@@ -98,7 +98,7 @@
       HttpRequest request = processor
             .createRequest(method, "ralphie", 2, 1, withFile("/etc/jclouds", "foo".getBytes()));
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(
             request,
@@ -120,7 +120,7 @@
       HttpRequest request = processor.createRequest(method, "ralphie", 2, 1,
             withMetadata(ImmutableMap.of("foo", "bar")));
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request,
             "{\"server\":{\"name\":\"ralphie\",\"imageRef\":\"2\",\"flavorRef\":\"1\",\"metadata\":{\"foo\":\"bar\"}}}",
@@ -138,7 +138,7 @@
       Method method = NovaAsyncClient.class.getMethod("deleteImage", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "DELETE http://endpoint/vapiversion/images/2 HTTP/1.1");
+      assertRequestLineEquals(request, "DELETE http://endpoint/vapi-version/images/2 HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -153,7 +153,7 @@
       Method method = NovaAsyncClient.class.getMethod("listServers", listOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/servers?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -171,7 +171,7 @@
       HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2));
 
       assertRequestLineEquals(request,
-            "GET http://endpoint/vapiversion/servers?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+            "GET http://endpoint/vapi-version/servers?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -186,7 +186,7 @@
       Method method = NovaAsyncClient.class.getMethod("listServers", listOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method, withDetails());
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/detail?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/servers/detail?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -201,7 +201,7 @@
       Method method = NovaAsyncClient.class.getMethod("getServer", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/2?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/servers/2?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -216,7 +216,7 @@
       Method method = NovaAsyncClient.class.getMethod("getServer", String.class);
       HttpRequest request = processor.createRequest(method, "dfdcd0a6-0a2f-11e1-8505-2837371c69ae");
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/dfdcd0a6-0a2f-11e1-8505-2837371c69ae?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/servers/dfdcd0a6-0a2f-11e1-8505-2837371c69ae?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -231,7 +231,7 @@
       Method method = NovaAsyncClient.class.getMethod("listFlavors", listOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/flavors?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/flavors?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -247,7 +247,7 @@
       HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2));
 
       assertRequestLineEquals(request,
-            "GET http://endpoint/vapiversion/flavors?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+            "GET http://endpoint/vapi-version/flavors?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -262,7 +262,7 @@
       Method method = NovaAsyncClient.class.getMethod("listFlavors", listOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method, withDetails());
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/flavors/detail?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/flavors/detail?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -278,7 +278,7 @@
       HttpRequest request = processor.createRequest(method, withDetails().changesSince(now).maxResults(1).startAt(2));
 
       assertRequestLineEquals(request,
-            "GET http://endpoint/vapiversion/flavors/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+            "GET http://endpoint/vapi-version/flavors/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -293,7 +293,7 @@
       Method method = NovaAsyncClient.class.getMethod("getFlavor", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/flavors/2?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/flavors/2?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -308,7 +308,7 @@
       Method method = NovaAsyncClient.class.getMethod("getFlavor", String.class);
       HttpRequest request = processor.createRequest(method, "209904b6-0a30-11e1-a0f0-2837371c69ae");
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/flavors/209904b6-0a30-11e1-a0f0-2837371c69ae?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/flavors/209904b6-0a30-11e1-a0f0-2837371c69ae?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -323,7 +323,7 @@
       Method method = NovaAsyncClient.class.getMethod("listImages", listOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/images?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/images?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -338,7 +338,7 @@
       Method method = NovaAsyncClient.class.getMethod("listImages", listOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method, withDetails());
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/images/detail?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/images/detail?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -354,7 +354,7 @@
       HttpRequest request = processor.createRequest(method, changesSince(now).maxResults(1).startAt(2));
 
       assertRequestLineEquals(request,
-            "GET http://endpoint/vapiversion/images?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+            "GET http://endpoint/vapi-version/images?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -370,7 +370,7 @@
       HttpRequest request = processor.createRequest(method, withDetails().changesSince(now).maxResults(1).startAt(2));
 
       assertRequestLineEquals(request,
-            "GET http://endpoint/vapiversion/images/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
+            "GET http://endpoint/vapi-version/images/detail?format=json&changes-since=10000&limit=1&offset=2 HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -385,7 +385,7 @@
       Method method = NovaAsyncClient.class.getMethod("getImage", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/images/2?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/images/2?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -400,7 +400,7 @@
       Method method = NovaAsyncClient.class.getMethod("getImage", int.class);
       HttpRequest request = processor.createRequest(method, "3bd52d90-0a30-11e1-83f5-2837371c69ae");
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/images/3bd52d90-0a30-11e1-83f5-2837371c69ae?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/images/3bd52d90-0a30-11e1-83f5-2837371c69ae?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -415,7 +415,7 @@
       Method method = NovaAsyncClient.class.getMethod("deleteServer", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "DELETE http://endpoint/vapiversion/servers/2 HTTP/1.1");
+      assertRequestLineEquals(request, "DELETE http://endpoint/vapi-version/servers/2 HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -430,7 +430,7 @@
       Method method = NovaAsyncClient.class.getMethod("deleteServer", String.class);
       HttpRequest request = processor.createRequest(method, "db8a1ac6-0a35-11e1-a42f-2837371c69ae");
 
-      assertRequestLineEquals(request, "DELETE http://endpoint/vapiversion/servers/db8a1ac6-0a35-11e1-a42f-2837371c69ae HTTP/1.1");
+      assertRequestLineEquals(request, "DELETE http://endpoint/vapi-version/servers/db8a1ac6-0a35-11e1-a42f-2837371c69ae HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -445,7 +445,7 @@
       Method method = NovaAsyncClient.class.getMethod("changeAdminPass", int.class, String.class);
       HttpRequest request = processor.createRequest(method, 2, "foo");
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action HTTP/1.1");

+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers/2/action HTTP/1.1");

       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, "{\"changePassword\":{\"adminPass\":\"foo\"}}", MediaType.APPLICATION_JSON, false);
 
@@ -461,7 +461,7 @@
       Method method = NovaAsyncClient.class.getMethod("renameServer", int.class, String.class);
       HttpRequest request = processor.createRequest(method, 2, "foo");
 
-      assertRequestLineEquals(request, "PUT http://endpoint/vapiversion/servers/2 HTTP/1.1");
+      assertRequestLineEquals(request, "PUT http://endpoint/vapi-version/servers/2 HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, "{\"server\":{\"name\":\"foo\"}}", MediaType.APPLICATION_JSON, false);
 
@@ -477,7 +477,7 @@
       Method method = NovaAsyncClient.class.getMethod("getAddresses", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/2/ips?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/servers/2/ips?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -492,7 +492,7 @@
       Method method = NovaAsyncClient.class.getMethod("listPublicAddresses", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/2/ips/public?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/servers/2/ips/public?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -508,7 +508,7 @@
       Method method = NovaAsyncClient.class.getMethod("listPrivateAddresses", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "GET http://endpoint/vapiversion/servers/2/ips/private?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "GET http://endpoint/vapi-version/servers/2/ips/private?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, null, null, false);
 
@@ -524,7 +524,7 @@
       Method method = NovaAsyncClient.class.getMethod("createImageFromServer", String.class, int.class);
       HttpRequest request = processor.createRequest(method, "ralphie", 2);
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/images?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/images?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
       assertPayloadEquals(request, "{\"image\":{\"serverId\":2,\"name\":\"ralphie\"}}", MediaType.APPLICATION_JSON,
             false);
@@ -546,7 +546,7 @@
             rebuildServerOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method, 3);
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/3/action?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers/3/action?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, "{\"rebuild\":{}}", MediaType.APPLICATION_JSON, false);
 
@@ -563,7 +563,7 @@
             rebuildServerOptionsVarargsClass);
       HttpRequest request = processor.createRequest(method, 3, withImage("2"));
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/3/action?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers/3/action?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, "{\"rebuild\":{\"imageRef\":\"2\"}}", MediaType.APPLICATION_JSON, false);
 
@@ -579,7 +579,7 @@
       Method method = NovaAsyncClient.class.getMethod("rebootServer", int.class, RebootType.class);
       HttpRequest request = processor.createRequest(method, 2, RebootType.HARD);
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers/2/action?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, "{\"reboot\":{\"type\":\"HARD\"}}", MediaType.APPLICATION_JSON, false);
 
@@ -595,7 +595,7 @@
       Method method = NovaAsyncClient.class.getMethod("resizeServer", int.class, int.class);
       HttpRequest request = processor.createRequest(method, 2, 3);
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers/2/action?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, "{\"resize\":{\"flavorId\":3}}", MediaType.APPLICATION_JSON, false);
 
@@ -611,7 +611,7 @@
       Method method = NovaAsyncClient.class.getMethod("confirmResizeServer", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers/2/action?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, "{\"confirmResize\":null}", MediaType.APPLICATION_JSON, false);
 
@@ -626,7 +626,7 @@
       Method method = NovaAsyncClient.class.getMethod("revertResizeServer", int.class);
       HttpRequest request = processor.createRequest(method, 2);
 
-      assertRequestLineEquals(request, "POST http://endpoint/vapiversion/servers/2/action?format=json HTTP/1.1");
+      assertRequestLineEquals(request, "POST http://endpoint/vapi-version/servers/2/action?format=json HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Accept: */*\n");
       assertPayloadEquals(request, "{\"revertResize\":null}", MediaType.APPLICATION_JSON, false);
 
@@ -665,7 +665,7 @@
 
       @Override
       protected URI provideServerUrl(AuthenticationResponse response) {
-         return URI.create("http://endpoint/vapiversion");
+         return URI.create("http://endpoint/vapi-version");
       }
 
    }
@@ -680,7 +680,7 @@
    @Override
    protected Properties getProperties() {
       Properties overrides = new Properties();
-      overrides.setProperty(PROPERTY_API_VERSION, "apiversion");
+      overrides.setProperty(PROPERTY_API_VERSION, "api-version");
       overrides.setProperty(provider + ".endpoint", "http://endpoint");
       overrides.setProperty(provider + ".contextbuilder", NovaContextBuilder.class.getName());
       return overrides;
diff --git a/apis/s3/pom.xml b/apis/s3/pom.xml
index 59af2a8..0dd8533 100644
--- a/apis/s3/pom.xml
+++ b/apis/s3/pom.xml
@@ -36,7 +36,8 @@
   <properties>
     <test.initializer>org.jclouds.s3.blobstore.integration.S3TestInitializer</test.initializer>
     <test.s3.endpoint>https://s3.amazonaws.com</test.s3.endpoint>
-    <test.s3.apiversion>2006-03-01</test.s3.apiversion>
+    <test.s3.api-version>2006-03-01</test.s3.api-version>
+    <test.s3.build-version></test.s3.build-version>
     <test.s3.identity>${test.aws.identity}</test.s3.identity>
     <test.s3.credential>${test.aws.credential}</test.s3.credential>
   </properties>
@@ -108,7 +109,8 @@
                     <jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
                     <jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
                     <test.s3.endpoint>${test.s3.endpoint}</test.s3.endpoint>
-                    <test.s3.apiversion>${test.s3.apiversion}</test.s3.apiversion>
+                    <test.s3.api-version>${test.s3.api-version}</test.s3.api-version>
+                    <test.s3.build-version>${test.s3.build-version}</test.s3.build-version>
                     <test.s3.identity>${test.s3.identity}</test.s3.identity>
                     <test.s3.credential>${test.s3.credential}</test.s3.credential>
                   </systemPropertyVariables>
diff --git a/apis/s3/src/main/java/org/jclouds/s3/config/S3RestClientModule.java b/apis/s3/src/main/java/org/jclouds/s3/config/S3RestClientModule.java
index 5265f41..5b784c9 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/config/S3RestClientModule.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/config/S3RestClientModule.java
@@ -21,7 +21,6 @@
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.javax.annotation.Nullable;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
@@ -30,10 +29,12 @@
 import org.jclouds.date.DateService;
 import org.jclouds.date.TimeStamp;
 import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpRetryHandler;
 import org.jclouds.http.RequiresHttp;
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
 import org.jclouds.http.annotation.ServerError;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Region;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.RequestSigner;
@@ -42,6 +43,7 @@
 import org.jclouds.s3.S3Client;
 import org.jclouds.s3.filters.RequestAuthorizeSignature;
 import org.jclouds.s3.handlers.ParseS3ErrorFromXmlContent;
+import org.jclouds.s3.handlers.S3RedirectionRetryHandler;
 
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
@@ -100,6 +102,11 @@
    protected RequestSigner provideRequestSigner(RequestAuthorizeSignature in) {
       return in;
    }
+   
+   @Override
+   protected void bindRetryHandlers() {
+      bind(HttpRetryHandler.class).annotatedWith(Redirection.class).to(S3RedirectionRetryHandler.class);
+   }
 
    @Provides
    @TimeStamp
diff --git a/common/aws/src/main/java/org/jclouds/aws/handlers/AWSRedirectionRetryHandler.java b/apis/s3/src/main/java/org/jclouds/s3/handlers/S3RedirectionRetryHandler.java
similarity index 90%
rename from common/aws/src/main/java/org/jclouds/aws/handlers/AWSRedirectionRetryHandler.java
rename to apis/s3/src/main/java/org/jclouds/s3/handlers/S3RedirectionRetryHandler.java
index 588c1e6..8871215 100644
--- a/common/aws/src/main/java/org/jclouds/aws/handlers/AWSRedirectionRetryHandler.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/handlers/S3RedirectionRetryHandler.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.handlers;
+package org.jclouds.s3.handlers;
 
 import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
 
@@ -40,11 +40,11 @@
  * @author Adrian Cole
  */
 @Singleton
-public class AWSRedirectionRetryHandler extends RedirectionRetryHandler {
+public class S3RedirectionRetryHandler extends RedirectionRetryHandler {
    private final AWSUtils utils;
 
    @Inject
-   public AWSRedirectionRetryHandler(Provider<UriBuilder> uriBuilderProvider,
+   public S3RedirectionRetryHandler(Provider<UriBuilder> uriBuilderProvider,
          BackoffLimitedRetryHandler backoffHandler, AWSUtils utils) {
       super(uriBuilderProvider, backoffHandler);
       this.utils = utils;
@@ -54,7 +54,8 @@
    public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
       if (response.getFirstHeaderOrNull(HttpHeaders.LOCATION) == null
             && (response.getStatusCode() == 301 || response.getStatusCode() == 307)) {
-         if (command.getCurrentRequest().getMethod() == HttpMethod.HEAD) {
+         if (command.getCurrentRequest().getMethod().equals(HttpMethod.HEAD)) {
+            command.incrementRedirectCount();
             command.setCurrentRequest(command.getCurrentRequest().toBuilder().method("GET").build());
             return true;
          } else {
diff --git a/apis/s3/src/test/java/org/jclouds/s3/S3AsyncClientTest.java b/apis/s3/src/test/java/org/jclouds/s3/S3AsyncClientTest.java
index 4c4676f..94b088c 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/S3AsyncClientTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/S3AsyncClientTest.java
@@ -54,6 +54,7 @@
 import org.jclouds.s3.functions.ParseObjectMetadataFromHeaders;
 import org.jclouds.s3.functions.ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState;
 import org.jclouds.s3.functions.ReturnTrueOn404OrNotFoundFalseOnIllegalState;
+import org.jclouds.s3.internal.BaseS3AsyncClientTest;
 import org.jclouds.s3.options.CopyObjectOptions;
 import org.jclouds.s3.options.ListBucketOptions;
 import org.jclouds.s3.options.PutBucketOptions;
diff --git a/apis/s3/src/test/java/org/jclouds/s3/S3ClientExpectTest.java b/apis/s3/src/test/java/org/jclouds/s3/S3ClientExpectTest.java
new file mode 100644
index 0000000..4fb0c87
--- /dev/null
+++ b/apis/s3/src/test/java/org/jclouds/s3/S3ClientExpectTest.java
@@ -0,0 +1,55 @@
+/**
+ * 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.s3;
+
+import java.net.URI;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.s3.internal.BaseS3ClientExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "S3ClientExpectTest")
+public class S3ClientExpectTest extends BaseS3ClientExpectTest {
+
+   public void bucketExistsReturnsTrueOn200AndFalseOn404() {
+      
+      HttpRequest bucketFooExists = HttpRequest.builder().method("HEAD").endpoint(
+               URI.create("https://foo.s3.amazonaws.com/?max-keys=0")).headers(
+               ImmutableMultimap.<String, String> builder()
+                  .put("Host", "foo.s3.amazonaws.com")
+                  .put("Date", CONSTANT_DATE)
+                  .put("Authorization", "AWS identity:86P4BBb7xT+gBqq7jxM8Tc28ktY=")
+                  .build()).build();
+      
+      S3Client clientWhenBucketExists = requestSendsResponse(bucketFooExists, HttpResponse.builder().statusCode(200).build());
+      assert clientWhenBucketExists.bucketExists("foo");
+      
+      S3Client clientWhenBucketDoesntExist = requestSendsResponse(bucketFooExists, HttpResponse.builder().statusCode(404).build());
+      assert !clientWhenBucketDoesntExist.bucketExists("foo");
+      
+   }
+
+}
\ No newline at end of file
diff --git a/apis/s3/src/test/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfiguredNoPathTest.java b/apis/s3/src/test/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfiguredNoPathTest.java
index 960ca8e..b4daa5c 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfiguredNoPathTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfiguredNoPathTest.java
@@ -26,8 +26,8 @@
 
 import org.jclouds.rest.internal.GeneratedHttpRequest;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
-import org.jclouds.s3.BaseS3AsyncClientTest;
 import org.jclouds.s3.S3AsyncClient;
+import org.jclouds.s3.internal.BaseS3AsyncClientTest;
 import org.testng.annotations.Test;
 
 import com.google.inject.TypeLiteral;
diff --git a/apis/s3/src/test/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfiguredTest.java b/apis/s3/src/test/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfiguredTest.java
index 0d2c856..a71b488 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfiguredTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfiguredTest.java
@@ -29,8 +29,8 @@
 
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
-import org.jclouds.s3.BaseS3AsyncClientTest;
 import org.jclouds.s3.S3AsyncClient;
+import org.jclouds.s3.internal.BaseS3AsyncClientTest;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
diff --git a/apis/s3/src/test/java/org/jclouds/s3/binders/BindNoBucketLoggingToXmlPayloadTest.java b/apis/s3/src/test/java/org/jclouds/s3/binders/BindNoBucketLoggingToXmlPayloadTest.java
index 0947c06..8193552 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/binders/BindNoBucketLoggingToXmlPayloadTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/binders/BindNoBucketLoggingToXmlPayloadTest.java
@@ -25,8 +25,8 @@
 
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
-import org.jclouds.s3.BaseS3AsyncClientTest;
 import org.jclouds.s3.S3AsyncClient;
+import org.jclouds.s3.internal.BaseS3AsyncClientTest;
 import org.testng.annotations.Test;
 
 import com.google.inject.TypeLiteral;
diff --git a/apis/s3/src/test/java/org/jclouds/s3/binders/BindS3ObjectMetadataToRequestTest.java b/apis/s3/src/test/java/org/jclouds/s3/binders/BindS3ObjectMetadataToRequestTest.java
index 4d9664f..34d4b5f 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/binders/BindS3ObjectMetadataToRequestTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/binders/BindS3ObjectMetadataToRequestTest.java
@@ -30,9 +30,9 @@
 import org.jclouds.io.Payload;
 import org.jclouds.io.Payloads;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
-import org.jclouds.s3.BaseS3AsyncClientTest;
 import org.jclouds.s3.S3AsyncClient;
 import org.jclouds.s3.domain.S3Object;
+import org.jclouds.s3.internal.BaseS3AsyncClientTest;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMap;
diff --git a/apis/s3/src/test/java/org/jclouds/s3/blobstore/S3BlobRequestSignerTest.java b/apis/s3/src/test/java/org/jclouds/s3/blobstore/S3BlobRequestSignerTest.java
index 30c6998..c88a778 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/blobstore/S3BlobRequestSignerTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/blobstore/S3BlobRequestSignerTest.java
@@ -32,10 +32,10 @@
 import org.jclouds.http.RequiresHttp;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
-import org.jclouds.s3.BaseS3AsyncClientTest;
 import org.jclouds.s3.S3AsyncClient;
 import org.jclouds.s3.S3Client;
 import org.jclouds.s3.config.S3RestClientModule;
+import org.jclouds.s3.internal.BaseS3AsyncClientTest;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
diff --git a/apis/s3/src/test/java/org/jclouds/s3/blobstore/integration/S3TestInitializer.java b/apis/s3/src/test/java/org/jclouds/s3/blobstore/integration/S3TestInitializer.java
index 54a0660..4598042 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/blobstore/integration/S3TestInitializer.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/blobstore/integration/S3TestInitializer.java
@@ -41,10 +41,10 @@
    }
 
    @Override
-   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiversion,
-            String app, String identity, String credential) throws IOException {
+   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiVersion,
+            String buildVersion, String app, String identity, String credential) throws IOException {
       return new BlobStoreContextFactory().createContext(provider, ImmutableSet.of(configurationModule,
-               new Log4JLoggingModule()), setupProperties(endpoint, apiversion, identity, credential));
+               new Log4JLoggingModule()), setupProperties(endpoint, apiVersion, buildVersion, identity, credential));
    }
 
 }
diff --git a/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureTest.java b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureTest.java
index d23d61f..4be1ded 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureTest.java
@@ -29,11 +29,11 @@
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.internal.GeneratedHttpRequest;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
-import org.jclouds.s3.BaseS3AsyncClientTest;
 import org.jclouds.s3.S3AsyncClient;
 import org.jclouds.s3.domain.AccessControlList;
 import org.jclouds.s3.domain.CannedAccessPolicy;
 import org.jclouds.s3.domain.S3Object;
+import org.jclouds.s3.internal.BaseS3AsyncClientTest;
 import org.jclouds.s3.options.PutObjectOptions;
 import org.jclouds.s3.reference.S3Headers;
 import org.testng.annotations.DataProvider;
diff --git a/apis/s3/src/test/java/org/jclouds/s3/handlers/S3RedirectionRetryHandlerExpectTest.java b/apis/s3/src/test/java/org/jclouds/s3/handlers/S3RedirectionRetryHandlerExpectTest.java
new file mode 100644
index 0000000..eae7e10
--- /dev/null
+++ b/apis/s3/src/test/java/org/jclouds/s3/handlers/S3RedirectionRetryHandlerExpectTest.java
@@ -0,0 +1,61 @@
+/**
+ * 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.s3.handlers;
+
+import java.net.URI;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.s3.S3Client;
+import org.jclouds.s3.internal.BaseS3ClientExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "S3RedirectionRetryHandlerExpectTest")
+public class S3RedirectionRetryHandlerExpectTest extends BaseS3ClientExpectTest {
+
+   public void testRedirectOnHeadBucketChangesRequestToGetBucket() {
+
+      HttpRequest bucketFooExists = HttpRequest.builder().method("HEAD").endpoint(
+               URI.create("https://foo.s3.amazonaws.com/?max-keys=0")).headers(
+               ImmutableMultimap.<String, String> builder().put("Host", "foo.s3.amazonaws.com").put("Date",
+                        CONSTANT_DATE).put("Authorization", "AWS identity:86P4BBb7xT+gBqq7jxM8Tc28ktY=").build())
+               .build();
+
+      HttpResponse redirectResponse = HttpResponse.builder().statusCode(301).build();
+
+      HttpRequest bucketFooExistsNowUsesGET = HttpRequest.builder().method("GET").endpoint(
+               URI.create("https://foo.s3.amazonaws.com/?max-keys=0")).headers(
+               ImmutableMultimap.<String, String> builder().put("Host", "foo.s3.amazonaws.com").put("Date",
+                        CONSTANT_DATE).put("Authorization", "AWS identity:ZWVz2v/jGB+ZMmijoyfH9mFMPo0=").build())
+               .build();
+
+      HttpResponse success = HttpResponse.builder().statusCode(200).build();
+
+      S3Client clientWhenBucketExists = requestsSendResponses(bucketFooExists, redirectResponse, bucketFooExistsNowUsesGET, success);
+      
+      assert clientWhenBucketExists.bucketExists("foo");
+
+   }
+}
\ No newline at end of file
diff --git a/apis/s3/src/test/java/org/jclouds/s3/BaseS3AsyncClientTest.java b/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3AsyncClientTest.java
similarity index 94%
rename from apis/s3/src/test/java/org/jclouds/s3/BaseS3AsyncClientTest.java
rename to apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3AsyncClientTest.java
index 2cfa94d..f161cf8 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/BaseS3AsyncClientTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3AsyncClientTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.s3;
+package org.jclouds.s3.internal;
 
 import static org.testng.Assert.assertEquals;
 
@@ -27,6 +27,9 @@
 import org.jclouds.rest.RestClientTest;
 import org.jclouds.rest.RestContextFactory;
 import org.jclouds.rest.RestContextSpec;
+import org.jclouds.s3.S3AsyncClient;
+import org.jclouds.s3.S3ContextBuilder;
+import org.jclouds.s3.S3PropertiesBuilder;
 import org.jclouds.s3.blobstore.functions.BlobToObject;
 import org.jclouds.s3.filters.RequestAuthorizeSignature;
 import org.testng.annotations.BeforeClass;
diff --git a/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3ClientExpectTest.java b/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3ClientExpectTest.java
new file mode 100644
index 0000000..57318b5
--- /dev/null
+++ b/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3ClientExpectTest.java
@@ -0,0 +1,75 @@
+/**
+ * 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.s3.internal;
+
+import java.util.Properties;
+
+import org.jclouds.date.TimeStamp;
+import org.jclouds.http.RequiresHttp;
+import org.jclouds.rest.BaseRestClientExpectTest;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.s3.S3AsyncClient;
+import org.jclouds.s3.S3Client;
+import org.jclouds.s3.S3ContextBuilder;
+import org.jclouds.s3.S3PropertiesBuilder;
+import org.jclouds.s3.config.S3RestClientModule;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Module;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public abstract class BaseS3ClientExpectTest extends BaseRestClientExpectTest<S3Client> {
+
+   protected static final String CONSTANT_DATE = "2009-11-08T15:54:08.897Z";
+
+   public BaseS3ClientExpectTest() {
+      provider = "s3";
+   }
+
+   @Override
+   protected Properties setupRestProperties() {
+      Properties overrides = new Properties();
+      overrides.setProperty(provider + ".endpoint", "https://s3.amazonaws.com");
+      overrides.setProperty(provider + ".propertiesbuilder", S3PropertiesBuilder.class.getName());
+      overrides.setProperty(provider + ".contextbuilder", S3ContextBuilder.class.getName());
+      return overrides;
+   }
+
+   @RequiresHttp
+   @ConfiguresRestClient
+   private static final class TestS3RestClientModule extends S3RestClientModule<S3Client, S3AsyncClient> {
+
+      public TestS3RestClientModule() {
+         super(S3Client.class, S3AsyncClient.class);
+      }
+
+      @Override
+      protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
+         return CONSTANT_DATE;
+      }
+   }
+
+   @Override
+   protected Module createModule() {
+      return new TestS3RestClientModule();
+   }
+}
\ No newline at end of file
diff --git a/apis/swift/pom.xml b/apis/swift/pom.xml
index 3290e70..a998434 100644
--- a/apis/swift/pom.xml
+++ b/apis/swift/pom.xml
@@ -36,7 +36,8 @@
   <properties>
     <test.initializer>org.jclouds.openstack.swift.blobstore.integration.SwiftTestInitializer</test.initializer>
     <test.swift.endpoint>http://localhost:11000</test.swift.endpoint>
-    <test.swift.apiversion>1.0</test.swift.apiversion>
+    <test.swift.api-version>1.0</test.swift.api-version>
+    <test.swift.build-version></test.swift.build-version>
     <test.swift.identity>test:tester</test.swift.identity>
     <test.swift.credential>testing</test.swift.credential>
   </properties>
@@ -108,7 +109,8 @@
                     <jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
                     <jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
                     <test.swift.endpoint>${test.swift.endpoint}</test.swift.endpoint>
-                    <test.swift.apiversion>${test.swift.apiversion}</test.swift.apiversion>
+                    <test.swift.api-version>${test.swift.api-version}</test.swift.api-version>
+                    <test.swift.build-version>${test.swift.build-version}</test.swift.build-version>
                     <test.swift.identity>${test.swift.identity}</test.swift.identity>
                     <test.swift.credential>${test.swift.credential}</test.swift.credential>
                   </systemPropertyVariables>
diff --git a/apis/swift/src/main/java/org/jclouds/openstack/swift/config/BaseSwiftRestClientModule.java b/apis/swift/src/main/java/org/jclouds/openstack/swift/config/BaseSwiftRestClientModule.java
index fe311a6..b0a1445 100644
--- a/apis/swift/src/main/java/org/jclouds/openstack/swift/config/BaseSwiftRestClientModule.java
+++ b/apis/swift/src/main/java/org/jclouds/openstack/swift/config/BaseSwiftRestClientModule.java
@@ -23,6 +23,7 @@
 import javax.inject.Singleton;
 
 import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpRetryHandler;
 import org.jclouds.http.RequiresHttp;
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
@@ -31,6 +32,7 @@
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
 import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
 import org.jclouds.openstack.config.OpenStackAuthenticationModule;
+import org.jclouds.openstack.handlers.RetryOnRenew;
 import org.jclouds.openstack.reference.AuthHeaders;
 import org.jclouds.openstack.swift.CommonSwiftAsyncClient;
 import org.jclouds.openstack.swift.CommonSwiftClient;
@@ -76,6 +78,11 @@
       bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseSwiftErrorFromHttpResponse.class);
    }
 
+   @Override
+   protected void bindRetryHandlers() {
+      bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class);
+   }
+   
    @Provides
    @Singleton
    @Storage
diff --git a/apis/swift/src/test/java/org/jclouds/openstack/swift/blobstore/integration/SwiftTestInitializer.java b/apis/swift/src/test/java/org/jclouds/openstack/swift/blobstore/integration/SwiftTestInitializer.java
index 56b1783..6f51336 100644
--- a/apis/swift/src/test/java/org/jclouds/openstack/swift/blobstore/integration/SwiftTestInitializer.java
+++ b/apis/swift/src/test/java/org/jclouds/openstack/swift/blobstore/integration/SwiftTestInitializer.java
@@ -39,10 +39,10 @@
    }
 
    @Override
-   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiversion,
-            String app, String identity, String credential) throws IOException {
+   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiVersion,
+            String buildVersion, String app, String identity, String credential) throws IOException {
       return new BlobStoreContextFactory().createContext(provider, ImmutableSet.of(configurationModule,
-               new Log4JLoggingModule()), setupProperties(endpoint, apiversion, identity, credential));
+               new Log4JLoggingModule()), setupProperties(endpoint, apiVersion, buildVersion, identity, credential));
    }
 
 }
diff --git a/apis/vcloud/pom.xml b/apis/vcloud/pom.xml
index 744278b..d7a50e9 100644
--- a/apis/vcloud/pom.xml
+++ b/apis/vcloud/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.vcloud.endpoint>FIXME</test.vcloud.endpoint>
-    <test.vcloud.apiversion>1.0</test.vcloud.apiversion>
+    <test.vcloud.api-version>1.0</test.vcloud.api-version>
+    <test.vcloud.build-version></test.vcloud.build-version>
     <test.vcloud.identity>FIXME</test.vcloud.identity>
     <test.vcloud.credential>FIXME</test.vcloud.credential>
     <test.vcloud.image-id />
@@ -105,7 +106,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.vcloud.endpoint>${test.vcloud.endpoint}</test.vcloud.endpoint>
-                    <test.vcloud.apiversion>${test.vcloud.apiversion}</test.vcloud.apiversion>
+                    <test.vcloud.api-version>${test.vcloud.api-version}</test.vcloud.api-version>
+                    <test.vcloud.build-version>${test.vcloud.build-version}</test.vcloud.build-version>
                     <test.vcloud.identity>${test.vcloud.identity}</test.vcloud.identity>
                     <test.vcloud.credential>${test.vcloud.credential}</test.vcloud.credential>
                     <test.vcloud.image-id>${test.vcloud.image-id}</test.vcloud.image-id>
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/config/IsDefaultVDC.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/config/IsDefaultVDC.java
index f534314..29a3e7b 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/config/IsDefaultVDC.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/config/IsDefaultVDC.java
@@ -1,12 +1,8 @@
 /**
- *
- * Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
+ * 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.  The ASF licenses this file
+ * 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
@@ -19,7 +15,6 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- * ====================================================================
  */
 package org.jclouds.vcloud.compute.config;
 
diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudVersionsAsyncClientTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudVersionsAsyncClientTest.java
index 5b58c08..276261c 100644
--- a/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudVersionsAsyncClientTest.java
+++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudVersionsAsyncClientTest.java
@@ -81,7 +81,7 @@
 
    @Override
    public RestContextSpec<VCloudVersionsClient, VCloudVersionsAsyncClient> createContextSpec() {
-      return contextSpec("test", "http://localhost:8080", "1", "", "identity", "credential",
+      return contextSpec("test", "http://localhost:8080", "1", "", "", "identity", "credential",
                VCloudVersionsClient.class, VCloudVersionsAsyncClient.class);
    }
 
diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/internal/VCloudLoginAsyncClientTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/internal/VCloudLoginAsyncClientTest.java
index 87bb807..3cec095 100644
--- a/apis/vcloud/src/test/java/org/jclouds/vcloud/internal/VCloudLoginAsyncClientTest.java
+++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/internal/VCloudLoginAsyncClientTest.java
@@ -107,7 +107,7 @@
 
    @Override
    public RestContextSpec<VCloudLoginClient, VCloudLoginAsyncClient> createContextSpec() {
-      return contextSpec("test", "http://localhost:8080/login", "1", "", "identity", "credential",
+      return contextSpec("test", "http://localhost:8080/login", "1", "", "", "identity", "credential",
                VCloudLoginClient.class, VCloudLoginAsyncClient.class);
    }
 }
diff --git a/apis/walrus/pom.xml b/apis/walrus/pom.xml
index 5080196..0febe97 100644
--- a/apis/walrus/pom.xml
+++ b/apis/walrus/pom.xml
@@ -36,7 +36,8 @@
   <properties>
     <test.initializer>org.jclouds.walrus.blobstore.WalrusTestInitializer</test.initializer>
     <test.walrus.endpoint>http://ecc.eucalyptus.com:8773/services/Walrus</test.walrus.endpoint>
-    <test.walrus.apiversion>2006-03-01</test.walrus.apiversion>
+    <test.walrus.api-version>2006-03-01</test.walrus.api-version>
+    <test.walrus.build-version></test.walrus.build-version>
     <test.walrus.identity>${test.eucalyptus.identity}</test.walrus.identity>
     <test.walrus.credential>${test.eucalyptus.credential}</test.walrus.credential>
   </properties>
@@ -104,7 +105,8 @@
                     <jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
                     <jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
                     <test.walrus.endpoint>${test.walrus.endpoint}</test.walrus.endpoint>
-                    <test.walrus.apiversion>${test.walrus.apiversion}</test.walrus.apiversion>
+                    <test.walrus.api-version>${test.walrus.api-version}</test.walrus.api-version>
+                    <test.walrus.build-version>${test.walrus.build-version}</test.walrus.build-version>
                     <test.walrus.identity>${test.walrus.identity}</test.walrus.identity>
                     <test.walrus.credential>${test.walrus.credential}</test.walrus.credential>
                   </systemPropertyVariables>
diff --git a/archetypes/rest-client-archetype/src/main/resources/archetype-resources/pom.xml b/archetypes/rest-client-archetype/src/main/resources/archetype-resources/pom.xml
index ea96693..41cd0de 100644
--- a/archetypes/rest-client-archetype/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/rest-client-archetype/src/main/resources/archetype-resources/pom.xml
@@ -58,7 +58,8 @@
     <properties>
         <test.${lcaseProviderName}.identity>${providerIdentity}</test.${lcaseProviderName}.identity>
         <test.${lcaseProviderName}.credential>${providerCredential}</test.${lcaseProviderName}.credential>
-        <test.${lcaseProviderName}.apiversion>${providerApiVersion}</test.${lcaseProviderName}.apiversion>
+        <test.${lcaseProviderName}.api-version>${providerApiVersion}</test.${lcaseProviderName}.api-version>
+        <test.${lcaseProviderName}.build-version></test.${lcaseProviderName}.build-version>
         <test.${lcaseProviderName}.endpoint>${providerEndpoint}</test.${lcaseProviderName}.endpoint>
     </properties>
     <dependencies>
@@ -107,7 +108,8 @@
                                         <test.${lcaseProviderName}.identity>\$\{test.${lcaseProviderName}.identity\}</test.${lcaseProviderName}.identity>
                                         <test.${lcaseProviderName}.credential>\$\{test.${lcaseProviderName}.credential\}</test.${lcaseProviderName}.credential>
                                         <test.${lcaseProviderName}.endpoint>\$\{test.${lcaseProviderName}.endpoint\}</test.${lcaseProviderName}.endpoint>
-                                        <test.${lcaseProviderName}.apiversion>\$\{test.${lcaseProviderName}.apiversion\}</test.${lcaseProviderName}.apiversion>
+                                        <test.${lcaseProviderName}.api-version>\$\{test.${lcaseProviderName}.api-version\}</test.${lcaseProviderName}.api-version>
+                                        <test.${lcaseProviderName}.build-version>\$\{test.${lcaseProviderName}.build-version\}</test.${lcaseProviderName}.build-version>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/archetypes/rest-client-archetype/src/main/resources/archetype-resources/src/test/java/__providerName__ClientLiveTest.java b/archetypes/rest-client-archetype/src/main/resources/archetype-resources/src/test/java/__providerName__ClientLiveTest.java
index 3938b3a..df5b054 100644
--- a/archetypes/rest-client-archetype/src/main/resources/archetype-resources/src/test/java/__providerName__ClientLiveTest.java
+++ b/archetypes/rest-client-archetype/src/main/resources/archetype-resources/src/test/java/__providerName__ClientLiveTest.java
@@ -55,7 +55,7 @@
       identity = checkNotNull(System.getProperty("test.${lcaseProviderName}.identity"), "test.${lcaseProviderName}.identity");
       credential = checkNotNull(System.getProperty("test.${lcaseProviderName}.credential"), "test.${lcaseProviderName}.credential");
       endpoint = checkNotNull(System.getProperty("test.${lcaseProviderName}.endpoint"), "test.${lcaseProviderName}.endpoint");
-      apiVersion = checkNotNull(System.getProperty("test.${lcaseProviderName}.apiversion"), "test.${lcaseProviderName}.apiversion");
+      apiVersion = checkNotNull(System.getProperty("test.${lcaseProviderName}.api-version"), "test.${lcaseProviderName}.api-version");
    }
    
    @BeforeGroups(groups = { "live" })
diff --git a/blobstore/src/main/java/org/jclouds/blobstore/BlobStores.java b/blobstore/src/main/java/org/jclouds/blobstore/BlobStores.java
index 1aac1a0..fb3b418 100644
--- a/blobstore/src/main/java/org/jclouds/blobstore/BlobStores.java
+++ b/blobstore/src/main/java/org/jclouds/blobstore/BlobStores.java
@@ -1,3 +1,21 @@
+/**
+ * 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.blobstore;
 
 import java.util.Iterator;
diff --git a/blobstore/src/main/java/org/jclouds/blobstore/config/TransientBlobStoreContextModule.java b/blobstore/src/main/java/org/jclouds/blobstore/config/TransientBlobStoreContextModule.java
index 750db0d..31dd22d 100644
--- a/blobstore/src/main/java/org/jclouds/blobstore/config/TransientBlobStoreContextModule.java
+++ b/blobstore/src/main/java/org/jclouds/blobstore/config/TransientBlobStoreContextModule.java
@@ -21,8 +21,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
-import javax.inject.Singleton;
-
 import org.jclouds.blobstore.AsyncBlobStore;
 import org.jclouds.blobstore.BlobRequestSigner;
 import org.jclouds.blobstore.BlobStore;
@@ -36,7 +34,6 @@
 import org.jclouds.location.config.JustProviderLocationModule;
 
 import com.google.inject.AbstractModule;
-import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.TypeLiteral;
 
@@ -63,13 +60,9 @@
       install(new BlobStoreObjectModule());
       install(new BlobStoreMapModule());
       install(new JustProviderLocationModule());
+      bind(BlobStore.class).to(TransientBlobStore.class);
       bind(ConsistencyModel.class).toInstance(ConsistencyModel.STRICT);
       bind(BlobRequestSigner.class).to(TransientBlobRequestSigner.class);
    }
 
-   @Provides
-   @Singleton
-   BlobStore provide(TransientBlobStore in) {
-      return in;
-   }
 }
diff --git a/blobstore/src/test/java/org/jclouds/blobstore/BlobStoresTest.java b/blobstore/src/test/java/org/jclouds/blobstore/BlobStoresTest.java
index 42e35b9..9ea6d13 100644
--- a/blobstore/src/test/java/org/jclouds/blobstore/BlobStoresTest.java
+++ b/blobstore/src/test/java/org/jclouds/blobstore/BlobStoresTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.blobstore;
 
 import static org.easymock.classextension.EasyMock.createMock;
diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/TransientBlobStoreTestInitializer.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/TransientBlobStoreTestInitializer.java
index 10095cb..201287f 100644
--- a/blobstore/src/test/java/org/jclouds/blobstore/integration/TransientBlobStoreTestInitializer.java
+++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/TransientBlobStoreTestInitializer.java
@@ -32,10 +32,10 @@
 public class TransientBlobStoreTestInitializer extends BaseTestInitializer {
 
    @Override
-   protected BlobStoreContext createLiveContext(Module configurationModule, String url, String apiversion, String app,
+   protected BlobStoreContext createLiveContext(Module configurationModule, String url, String apiVersion,String buildVersion, String app,
             String identity, String key) throws IOException {
       return createStubContext();
    }
 
 
-}
\ No newline at end of file
+}
diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseTestInitializer.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseTestInitializer.java
index 02c8031..ecfb416 100644
--- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseTestInitializer.java
+++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseTestInitializer.java
@@ -37,7 +37,8 @@
       String app = System.getProperty("test.app");
       String identity = System.getProperty("test." + provider + ".identity");
       String credential = System.getProperty("test." + provider + ".credential");
-      String apiversion = System.getProperty("test." + provider + ".apiversion");
+      String apiVersion = System.getProperty("test." + provider + ".api-version");
+      String buildVersion = System.getProperty("test." + provider + ".build-version");
       if (endpoint != null)
          testContext.setAttribute("test." + provider + ".endpoint", endpoint);
       if (app != null)
@@ -46,16 +47,27 @@
          testContext.setAttribute("test." + provider + ".identity", identity);
       if (credential != null)
          testContext.setAttribute("test." + provider + ".credential", credential);
-      if (credential != null)
-         testContext.setAttribute("test." + provider + ".apiversion", apiversion);
+      if (apiVersion != null)
+         testContext.setAttribute("test." + provider + ".api-version", apiVersion);
+      if (buildVersion != null)
+         testContext.setAttribute("test." + provider + ".build-version", buildVersion);
       if (identity != null) {
-         return createLiveContext(configurationModule, endpoint, apiversion, app, identity, credential);
+         return createLiveContext(configurationModule, endpoint, apiVersion, buildVersion, app, identity, credential);
       } else {
          return createStubContext();
       }
    }
+   
 
-   protected Properties setupProperties(String endpoint, String apiversion, String identity, String credential) {
+   /**
+    * To be removed in jclouds 1.4
+    */
+   @Deprecated
+   protected Properties setupProperties(String endpoint, String apiVersion, String identity, String credential) {
+      return setupProperties(endpoint, apiVersion, "", identity, credential);
+   }
+
+   protected Properties setupProperties(String endpoint, String apiVersion, String buildVersion, String identity, String credential) {
       Properties overrides = new Properties();
       overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
       overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
@@ -65,8 +77,10 @@
          overrides.setProperty(provider + ".credential", credential);
       if (endpoint != null)
          overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
+      if (buildVersion != null)
+         overrides.setProperty(provider + ".build-version", buildVersion);
+      if (apiVersion != null)
+         overrides.setProperty(provider + ".api-version", apiVersion);
       return overrides;
    }
 
@@ -74,6 +88,6 @@
       return new BlobStoreContextFactory().createContext("transient", "foo", "bar");
    }
 
-   protected abstract BlobStoreContext createLiveContext(Module configurationModule, String url, String apiversion,
-            String app, String identity, String key) throws IOException;
-}
\ No newline at end of file
+   protected abstract BlobStoreContext createLiveContext(Module configurationModule, String url, String apiVersion,
+            String buildVersion, String app, String identity, String key) throws IOException;
+}
diff --git a/common/aws/src/main/java/org/jclouds/aws/config/AWSRestClientModule.java b/common/aws/src/main/java/org/jclouds/aws/config/AWSRestClientModule.java
index 2d7cc33..b4440ef 100644
--- a/common/aws/src/main/java/org/jclouds/aws/config/AWSRestClientModule.java
+++ b/common/aws/src/main/java/org/jclouds/aws/config/AWSRestClientModule.java
@@ -24,11 +24,9 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.jclouds.javax.annotation.Nullable;
 import javax.inject.Singleton;
 
 import org.jclouds.aws.handlers.AWSClientErrorRetryHandler;
-import org.jclouds.aws.handlers.AWSRedirectionRetryHandler;
 import org.jclouds.aws.handlers.ParseAWSErrorFromXmlContent;
 import org.jclouds.http.HttpErrorHandler;
 import org.jclouds.http.HttpRetryHandler;
@@ -36,6 +34,7 @@
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
 import org.jclouds.http.annotation.ServerError;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Provider;
 import org.jclouds.location.Region;
 import org.jclouds.location.config.ProvideRegionToURIViaProperties;
@@ -73,7 +72,6 @@
 
    @Override
    protected void bindRetryHandlers() {
-      bind(HttpRetryHandler.class).annotatedWith(Redirection.class).to(AWSRedirectionRetryHandler.class);
       bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(AWSClientErrorRetryHandler.class);
    }
 
diff --git a/common/aws/src/test/java/org/jclouds/aws/filters/FormSignerTest.java b/common/aws/src/test/java/org/jclouds/aws/filters/FormSignerTest.java
index e475aaf..12321d8 100644
--- a/common/aws/src/test/java/org/jclouds/aws/filters/FormSignerTest.java
+++ b/common/aws/src/test/java/org/jclouds/aws/filters/FormSignerTest.java
@@ -17,15 +17,13 @@
  * under the License.
  */
 package org.jclouds.aws.filters;
-
 import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
 import static org.testng.Assert.assertEquals;
 
-import java.util.List;
-import java.util.Map;
-
 import org.jclouds.PropertiesBuilder;
 import org.jclouds.date.TimeStamp;
+import org.jclouds.http.IntegrationTestAsyncClient;
+import org.jclouds.http.IntegrationTestClient;
 import org.jclouds.logging.config.NullLoggingModule;
 import org.jclouds.rest.RequestSigner;
 import org.jclouds.rest.RestContextBuilder;
@@ -49,8 +47,9 @@
 @Test(groups = "unit", testName = "FormSignerTest")
 public class FormSignerTest {
    @SuppressWarnings("unchecked")
-   public static final RestContextSpec<Map, List> DUMMY_SPEC = new RestContextSpec<Map, List>("provider", "endpoint",
-            "apiVersion", "", "identity", "credential", Map.class, List.class, PropertiesBuilder.class,
+   public static final RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> DUMMY_SPEC = new RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient>(
+            "provider", "endpoint", "apiVersion", "buildVersion", "", "identity", "credential",
+            IntegrationTestClient.class, IntegrationTestAsyncClient.class, PropertiesBuilder.class,
             (Class) RestContextBuilder.class, ImmutableList.<Module> of(new MockModule(), new NullLoggingModule(),
                      new AbstractModule() {
                         @Override
diff --git a/common/azure/src/test/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthenticationTest.java b/common/azure/src/test/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthenticationTest.java
index 690c8ad..d3f1bbd 100644
--- a/common/azure/src/test/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthenticationTest.java
+++ b/common/azure/src/test/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthenticationTest.java
@@ -22,8 +22,6 @@
 
 import java.io.IOException;
 import java.net.URI;
-import java.util.List;
-import java.util.Map;
 
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.HttpHeaders;
@@ -31,6 +29,8 @@
 import org.jclouds.PropertiesBuilder;
 import org.jclouds.azure.storage.config.AzureStorageRestClientModule;
 import org.jclouds.http.HttpRequest;
+import org.jclouds.http.IntegrationTestAsyncClient;
+import org.jclouds.http.IntegrationTestClient;
 import org.jclouds.logging.config.NullLoggingModule;
 import org.jclouds.rest.RestContextBuilder;
 import org.jclouds.rest.RestContextFactory;
@@ -137,10 +137,11 @@
    }
 
    @SuppressWarnings("unchecked")
-   public static final RestContextSpec<Map, List> DUMMY_SPEC = new RestContextSpec<Map, List>("provider", "endpoint",
-            "apiVersion", "", "identity", "credential", Map.class, List.class, PropertiesBuilder.class,
-            (Class) RestContextBuilder.class, ImmutableList.<Module> of(new MockModule(), new NullLoggingModule(),
-                     new AzureStorageRestClientModule<Exception, RuntimeException>(Exception.class,
-                              RuntimeException.class)));
+   public static final RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> DUMMY_SPEC = new RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient>(
+            "provider", "endpoint", "apiVersion", "buildVersion", "", "identity", "credential", IntegrationTestClient.class,
+            IntegrationTestAsyncClient.class, PropertiesBuilder.class, (Class) RestContextBuilder.class, ImmutableList
+                     .<Module> of(new MockModule(), new NullLoggingModule(),
+                              new AzureStorageRestClientModule<IntegrationTestClient, IntegrationTestAsyncClient>(IntegrationTestClient.class,
+                                       IntegrationTestAsyncClient.class)));
 
 }
\ No newline at end of file
diff --git a/common/openstack/src/main/java/org/jclouds/openstack/config/OpenStackAuthenticationModule.java b/common/openstack/src/main/java/org/jclouds/openstack/config/OpenStackAuthenticationModule.java
index f471e12..e7f180d 100644
--- a/common/openstack/src/main/java/org/jclouds/openstack/config/OpenStackAuthenticationModule.java
+++ b/common/openstack/src/main/java/org/jclouds/openstack/config/OpenStackAuthenticationModule.java
@@ -40,6 +40,10 @@
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
 import com.google.common.base.Throwables;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.UncheckedExecutionException;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 
@@ -104,10 +108,41 @@
 
    @Provides
    @Singleton
-   Supplier<AuthenticationResponse> provideAuthenticationResponseCache(
+   public LoadingCache<String,AuthenticationResponse> provideAuthenticationResponseCache2(
             final GetAuthenticationResponse getAuthenticationResponse) {
-      return Suppliers.memoizeWithExpiration(new RetryOnTimeOutExceptionSupplier<AuthenticationResponse>(
-               getAuthenticationResponse), 23, TimeUnit.HOURS);
+      
+      final RetryOnTimeOutExceptionSupplier<AuthenticationResponse> delegate = 
+               new RetryOnTimeOutExceptionSupplier<AuthenticationResponse>(getAuthenticationResponse);
+
+      CacheLoader<String, AuthenticationResponse> cacheLoader = new CacheLoader<String, AuthenticationResponse>() {
+         @Override
+         public AuthenticationResponse load(String key) throws Exception {
+            return delegate.get();
+         }
+      };
+      
+      LoadingCache<String, AuthenticationResponse> cache = CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS)
+               .build(cacheLoader);
+      
+      return cache;
+   }
+
+   @Provides
+   @Singleton
+   protected Supplier<AuthenticationResponse> provideAuthenticationResponseSupplier(
+            final LoadingCache<String,AuthenticationResponse> cache) {
+      return new Supplier<AuthenticationResponse>() {
+         @Override
+         public AuthenticationResponse get() {
+            try {
+               return cache.get("key");
+            } catch (UncheckedExecutionException e) {
+               throw Throwables.propagate(e.getCause());
+            } catch (ExecutionException e) {
+               throw Throwables.propagate(e.getCause());
+            }
+         }
+      };
    }
 
    @Provides
diff --git a/common/openstack/src/main/java/org/jclouds/openstack/handlers/RetryOnRenew.java b/common/openstack/src/main/java/org/jclouds/openstack/handlers/RetryOnRenew.java
new file mode 100644
index 0000000..edb39e1
--- /dev/null
+++ b/common/openstack/src/main/java/org/jclouds/openstack/handlers/RetryOnRenew.java
@@ -0,0 +1,98 @@
+/**
+ * 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.openstack.handlers;
+
+import static org.jclouds.http.HttpUtils.releasePayload;
+
+import java.io.IOException;
+
+import javax.annotation.Resource;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpRetryHandler;
+import org.jclouds.logging.Logger;
+import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
+import org.jclouds.openstack.reference.AuthHeaders;
+import org.jclouds.util.Strings2;
+
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Multimap;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * This will parse and set an appropriate exception on the command object.
+ * 
+ * @author Adrian Cole
+ * 
+ */
+@Singleton
+public class RetryOnRenew implements HttpRetryHandler {
+   @Resource
+   protected Logger logger = Logger.NULL;
+
+   // This doesn't work yet
+//   @Inject
+//   Supplier<AuthenticationResponse> providedAuthenticationResponseCache;
+
+   @Inject
+   LoadingCache<String,AuthenticationResponse> authenticationResponseCache;
+
+   @Override
+   public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
+      boolean retry = false; // default
+      try {
+         switch (response.getStatusCode()) {
+            case 401:
+               // Do not retry on 401 from authentication request
+               Multimap<String, String> headers = command.getCurrentRequest().getHeaders();
+               if (headers != null && headers.containsKey(AuthHeaders.AUTH_USER) && headers.containsKey(AuthHeaders.AUTH_KEY) &&
+                     !headers.containsKey(AuthHeaders.AUTH_TOKEN)) {
+                  retry = false;
+               } else {
+                  String content = parsePayloadOrNull(response);
+                  if (content != null && content.contains("lease renew")) {
+                     // Otherwise invalidate the token cache, to force reauthentication
+                     authenticationResponseCache.invalidateAll();
+                     retry = true;
+                  } else {
+                     retry = false;
+                  }
+               }
+               break;
+         }
+         return retry;
+         
+      } finally {
+         releasePayload(response);
+      }
+   }
+   
+   String parsePayloadOrNull(HttpResponse response) {
+      if (response.getPayload() != null) {
+         try {
+            return Strings2.toStringAndClose(response.getPayload().getInput());
+         } catch (IOException e) {
+            logger.warn(e, "exception reading error from response", response);
+         }
+      }
+      return null;
+   }
+}
diff --git a/common/openstack/src/test/java/org/jclouds/openstack/OpenStackAuthAsyncClientTest.java b/common/openstack/src/test/java/org/jclouds/openstack/OpenStackAuthAsyncClientTest.java
index 61edf2c..293174f 100644
--- a/common/openstack/src/test/java/org/jclouds/openstack/OpenStackAuthAsyncClientTest.java
+++ b/common/openstack/src/test/java/org/jclouds/openstack/OpenStackAuthAsyncClientTest.java
@@ -76,7 +76,7 @@
 
    @Override
    public RestContextSpec<OpenStackAuthClient, OpenStackAuthAsyncClient> createContextSpec() {
-      return contextSpec("test", "http://localhost:8080", "1.0", "", "identity", "credential",
+      return contextSpec("test", "http://localhost:8080", "1.0", "", "", "identity", "credential",
                OpenStackAuthClient.class, OpenStackAuthAsyncClient.class);
    }
 
diff --git a/common/openstack/src/test/java/org/jclouds/openstack/handlers/RetryOnRenewTest.java b/common/openstack/src/test/java/org/jclouds/openstack/handlers/RetryOnRenewTest.java
new file mode 100644
index 0000000..0aa578f
--- /dev/null
+++ b/common/openstack/src/test/java/org/jclouds/openstack/handlers/RetryOnRenewTest.java
@@ -0,0 +1,73 @@
+/**
+ * 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.openstack.handlers;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.classextension.EasyMock.createMock;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.easymock.classextension.EasyMock.verify;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.io.Payloads;
+import org.jclouds.openstack.OpenStackAuthAsyncClient.AuthenticationResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.cache.LoadingCache;
+
+/**
+ * Tests behavior of {@code RetryOnRenew} handler
+ * 
+ * @author grkvlt@apache.org
+ */
+@Test(groups = "unit", testName = "RetryOnRenewTest")
+public class RetryOnRenewTest {
+   @Test
+   public void test401ShouldRetry() {
+      HttpCommand command = createMock(HttpCommand.class);
+      HttpRequest request = createMock(HttpRequest.class);
+      HttpResponse response = createMock(HttpResponse.class);
+      @SuppressWarnings("unchecked")
+      LoadingCache<String,AuthenticationResponse> cache = createMock(LoadingCache.class);
+
+      expect(command.getCurrentRequest()).andReturn(request);
+
+      cache.invalidateAll();
+      expectLastCall();
+      
+      expect(response.getPayload()).andReturn(Payloads.newStringPayload("token expired, please renew")).anyTimes();
+      expect(response.getStatusCode()).andReturn(401).atLeastOnce();
+
+      replay(command);
+      replay(response);
+      replay(cache);
+      
+      RetryOnRenew retry = new RetryOnRenew();
+      retry.authenticationResponseCache = cache;
+      
+      assertTrue(retry.shouldRetryRequest(command, response));
+
+      verify(command);
+      verify(response);
+      verify(cache);
+   }
+}
diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/config/TerremarkVCloudComputeServiceContextModule.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/config/TerremarkVCloudComputeServiceContextModule.java
index 441b647..a507ac1 100644
--- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/config/TerremarkVCloudComputeServiceContextModule.java
+++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/compute/config/TerremarkVCloudComputeServiceContextModule.java
@@ -34,8 +34,6 @@
 import org.jclouds.compute.internal.ComputeServiceContextImpl;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy;
-import org.jclouds.rest.RestContext;
-import org.jclouds.rest.internal.RestContextImpl;
 import org.jclouds.trmk.vcloud_0_8.TerremarkVCloudAsyncClient;
 import org.jclouds.trmk.vcloud_0_8.TerremarkVCloudClient;
 import org.jclouds.trmk.vcloud_0_8.compute.TerremarkVCloudComputeService;
@@ -96,9 +94,6 @@
       bind(new TypeLiteral<ComputeServiceContext>() {
       }).to(new TypeLiteral<ComputeServiceContextImpl<TerremarkVCloudClient, TerremarkVCloudAsyncClient>>() {
       }).in(Scopes.SINGLETON);
-      bind(new TypeLiteral<RestContext<TerremarkVCloudClient, TerremarkVCloudAsyncClient>>() {
-      }).to(new TypeLiteral<RestContextImpl<TerremarkVCloudClient, TerremarkVCloudAsyncClient>>() {
-      }).in(Scopes.SINGLETON);
       bind(new TypeLiteral<Function<Org, Iterable<? extends Image>>>() {
       }).to(new TypeLiteral<ImagesInVCloudExpressOrg>() {
       });
diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/compute/suppliers/VAppTemplatesInOrgsLiveTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/compute/suppliers/VAppTemplatesInOrgsLiveTest.java
index 9f75e17..29374b3 100644
--- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/compute/suppliers/VAppTemplatesInOrgsLiveTest.java
+++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/compute/suppliers/VAppTemplatesInOrgsLiveTest.java
@@ -18,14 +18,13 @@
  */
 package org.jclouds.trmk.vcloud_0_8.compute.suppliers;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 
 import java.io.IOException;
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.lifecycle.Closer;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
@@ -48,38 +47,17 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class VAppTemplatesInOrgsLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "VAppTemplatesInOrgsLiveTest")
+public class VAppTemplatesInOrgsLiveTest extends BaseVersionedServiceLiveTest {
+   public VAppTemplatesInOrgsLiveTest() {
+      provider = "trmk-vcloudexpress";
+   }
 
    private TerremarkVCloudClient tmClient;
    private VAppTemplatesInOrgs parser;
    private Closer closer;
    private AllCatalogItemsInOrg allCatalogItemsInOrg;
-   protected String provider = "trmk-vcloudexpress";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
 
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-               + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/internal/TerremarkVCloudLoginAsyncClientTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/internal/TerremarkVCloudLoginAsyncClientTest.java
index 7b9df30..f728d20 100644
--- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/internal/TerremarkVCloudLoginAsyncClientTest.java
+++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/internal/TerremarkVCloudLoginAsyncClientTest.java
@@ -108,7 +108,7 @@
 
    @Override
    public RestContextSpec<VCloudLoginClient, TerremarkVCloudLoginAsyncClient> createContextSpec() {
-      return contextSpec("test", "http://localhost:8080/login", "1", "", "identity", "credential",
+      return contextSpec("test", "http://localhost:8080/login", "1", "", "", "identity", "credential",
                VCloudLoginClient.class, TerremarkVCloudLoginAsyncClient.class);
    }
 }
diff --git a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/internal/TerremarkVCloudVersionsAsyncClientTest.java b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/internal/TerremarkVCloudVersionsAsyncClientTest.java
index 49eeb94..1399a22 100644
--- a/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/internal/TerremarkVCloudVersionsAsyncClientTest.java
+++ b/common/trmk/src/test/java/org/jclouds/trmk/vcloud_0_8/internal/TerremarkVCloudVersionsAsyncClientTest.java
@@ -82,7 +82,7 @@
 
    @Override
    public RestContextSpec<VCloudVersionsClient, TerremarkVCloudVersionsAsyncClient> createContextSpec() {
-      return contextSpec("test", "http://localhost:8080", "1", "", "identity", "credential",
+      return contextSpec("test", "http://localhost:8080", "1", "", "", "identity", "credential",
                VCloudVersionsClient.class, TerremarkVCloudVersionsAsyncClient.class);
    }
 
diff --git a/compute/src/main/java/org/jclouds/compute/StandaloneComputeServiceContextSpec.java b/compute/src/main/java/org/jclouds/compute/StandaloneComputeServiceContextSpec.java
index f508c04..b226610 100644
--- a/compute/src/main/java/org/jclouds/compute/StandaloneComputeServiceContextSpec.java
+++ b/compute/src/main/java/org/jclouds/compute/StandaloneComputeServiceContextSpec.java
@@ -28,26 +28,26 @@
  * @author Adrian Cole
  */
 public class StandaloneComputeServiceContextSpec<D, N, H, I, L> extends RestContextSpec<D, D> {
-   public StandaloneComputeServiceContextSpec(String provider, String endpoint, String apiVersion, String iso3166Codes,
-            String identity, String credential, Class<D> driverClass,
+   public StandaloneComputeServiceContextSpec(String provider, String endpoint, String apiVersion, String buildVersion,
+            String iso3166Codes, String identity, String credential, Class<D> driverClass,
             Class<? extends StandaloneComputeServiceContextBuilder<D>> contextBuilderClass) {
-      this(provider, endpoint, apiVersion, iso3166Codes, identity, credential, driverClass, contextBuilderClass,
-               ImmutableSet.<Module> of());
+      this(provider, endpoint, apiVersion, buildVersion, iso3166Codes, identity, credential, driverClass,
+               contextBuilderClass, ImmutableSet.<Module> of());
    }
 
-   public StandaloneComputeServiceContextSpec(String provider, String endpoint, String apiVersion, String iso3166Codes,
-            String identity, String credential, Class<D> driverClass,
+   public StandaloneComputeServiceContextSpec(String provider, String endpoint, String apiVersion, String buildVersion,
+            String iso3166Codes, String identity, String credential, Class<D> driverClass,
             Class<? extends StandaloneComputeServiceContextBuilder<D>> contextBuilderClass, Iterable<Module> modules) {
-      this(provider, endpoint, apiVersion, iso3166Codes, identity, credential, driverClass, PropertiesBuilder.class,
-               contextBuilderClass, modules);
+      this(provider, endpoint, apiVersion, buildVersion, iso3166Codes, identity, credential, driverClass,
+               PropertiesBuilder.class, contextBuilderClass, modules);
    }
 
    @SuppressWarnings( { "unchecked", "rawtypes" })
-   public StandaloneComputeServiceContextSpec(String provider, String endpoint, String apiVersion, String iso3166Codes,
-            String identity, String credential, Class<D> driverClass,
+   public StandaloneComputeServiceContextSpec(String provider, String endpoint, String apiVersion, String buildVersion,
+            String iso3166Codes, String identity, String credential, Class<D> driverClass,
             Class<? extends PropertiesBuilder> propertiesBuilderClass,
             Class<? extends StandaloneComputeServiceContextBuilder<D>> contextBuilderClass, Iterable<Module> modules) {
-      super(provider, endpoint, apiVersion, iso3166Codes, identity, credential, driverClass, driverClass,
+      super(provider, endpoint, apiVersion, buildVersion, iso3166Codes, identity, credential, driverClass, driverClass,
                (Class) propertiesBuilderClass, (Class) contextBuilderClass, modules);
    }
 }
\ No newline at end of file
diff --git a/compute/src/main/java/org/jclouds/compute/callables/BlockUntilInitScriptStatusIsZeroThenReturnOutput.java b/compute/src/main/java/org/jclouds/compute/callables/BlockUntilInitScriptStatusIsZeroThenReturnOutput.java
index ded9d6d..8ebb352 100644
--- a/compute/src/main/java/org/jclouds/compute/callables/BlockUntilInitScriptStatusIsZeroThenReturnOutput.java
+++ b/compute/src/main/java/org/jclouds/compute/callables/BlockUntilInitScriptStatusIsZeroThenReturnOutput.java
@@ -26,7 +26,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 
 import org.jclouds.Constants;
@@ -109,8 +108,7 @@
     * Submits a thread that will either set the result of the future or the exception that took
     * place
     */
-   @PostConstruct
-   BlockUntilInitScriptStatusIsZeroThenReturnOutput init() {
+   public BlockUntilInitScriptStatusIsZeroThenReturnOutput init() {
       userThreads.submit(new Runnable() {
          @Override
          public void run() {
diff --git a/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java b/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java
index f05300b..8dbe9cd 100644
--- a/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java
+++ b/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java
@@ -21,7 +21,6 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 
-import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import javax.inject.Named;
 
@@ -61,7 +60,6 @@
       this.init = checkNotNull(init, "init");
    }
 
-   @PostConstruct
    public SudoAwareInitManager init() {
       ssh = sshFactory.apply(node);
       return this;
diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
index 71a8441..1c3cbbc 100644
--- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
@@ -81,7 +81,7 @@
 
    @Override
    protected void configure() {
-      install(new LocationModule(authException));
+      configureLocationModule();
       install(new ComputeServiceTimeoutsModule());
       bind(new TypeLiteral<Function<NodeMetadata, SshClient>>() {
       }).to(CreateSshClientOncePortIsListeningOnNode.class);
@@ -115,6 +115,10 @@
       install(new FactoryModuleBuilder().build(BlockUntilInitScriptStatusIsZeroThenReturnOutput.Factory.class));
    }
 
+   protected void configureLocationModule() {
+      install(new LocationModule(authException));
+   }
+
    @Singleton
    static class RunScriptOnNodeFactoryImpl implements RunScriptOnNode.Factory {
 
@@ -176,7 +180,7 @@
       template = provideTemplate(injector, template);
       String imageId = config.apply(provider + ".image-id");
       if (imageId == null)
-         imageId = config.apply("jclouds.image-id");
+         imageId = config.apply(ComputeServiceConstants.PROPERTY_IMAGE_ID);
       if (imageId != null)
          template.imageId(imageId);
       return template;
diff --git a/compute/src/main/java/org/jclouds/compute/config/ComputeServiceAdapterContextModule.java b/compute/src/main/java/org/jclouds/compute/config/ComputeServiceAdapterContextModule.java
index 5c915b3..a225389 100644
--- a/compute/src/main/java/org/jclouds/compute/config/ComputeServiceAdapterContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/config/ComputeServiceAdapterContextModule.java
@@ -18,11 +18,15 @@
  */
 package org.jclouds.compute.config;
 
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+
 import java.util.Set;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Singleton;
 
+import org.jclouds.collect.Memoized;
 import org.jclouds.collect.TransformingSetSupplier;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.ComputeServiceContext;
@@ -41,10 +45,13 @@
 import org.jclouds.compute.strategy.impl.AdaptingComputeServiceStrategies;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LoginCredentials;
+import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
 import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.TypeLiteral;
@@ -64,33 +71,39 @@
       this.asyncClientType = asyncClientType;
    }
 
-   @SuppressWarnings({ "unchecked", "rawtypes" })
+   @SuppressWarnings( { "unchecked", "rawtypes" })
    @Override
    protected void configure() {
       super.configure();
       bind(new TypeLiteral<ComputeServiceContext>() {
-      }).to((TypeLiteral) TypeLiteral.get(Types.newParameterizedType(ComputeServiceContextImpl.class, syncClientType,
-            asyncClientType))).in(Scopes.SINGLETON);
+      }).to(
+               (TypeLiteral) TypeLiteral.get(Types.newParameterizedType(ComputeServiceContextImpl.class,
+                        syncClientType, asyncClientType))).in(Scopes.SINGLETON);
+   }
+
+   @Override
+   protected void configureLocationModule() {
+      // configuring below
    }
 
    @Provides
    @Singleton
-   protected Supplier<Set<? extends Location>> provideLocations(final ComputeServiceAdapter<N, H, I, L> adapter,
-         Function<L, Location> transformer) {
-      return new TransformingSetSupplier<L, Location>(new Supplier<Iterable<L>>() {
-
-         @Override
-         public Iterable<L> get() {
-            return adapter.listLocations();
-         }
-
-      }, transformer);
+   @Memoized
+   protected Supplier<Set<? extends Location>> supplyLocationCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
+            final ComputeServiceAdapter<N, H, I, L> adapter, final Function<L, Location> transformer) {
+      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Set<? extends Location>>(authException,
+               seconds, new Supplier<Set<? extends Location>>() {
+                  @Override
+                  public Set<? extends Location> get() {
+                     return ImmutableSet.<Location> copyOf(Iterables.transform(adapter.listLocations(), transformer));
+                  }
+               });
    }
 
    @Provides
    @Singleton
    protected Supplier<Set<? extends Hardware>> provideHardware(final ComputeServiceAdapter<N, H, I, L> adapter,
-         Function<H, Hardware> transformer) {
+            Function<H, Hardware> transformer) {
       return new TransformingSetSupplier<H, Hardware>(new Supplier<Iterable<H>>() {
 
          @Override
@@ -104,7 +117,7 @@
    @Provides
    @Singleton
    protected Supplier<Set<? extends Image>> provideImages(final ComputeServiceAdapter<N, H, I, L> adapter,
-         Function<I, Image> transformer, AddDefaultCredentialsToImage addDefaultCredentialsToImage) {
+            Function<I, Image> transformer, AddDefaultCredentialsToImage addDefaultCredentialsToImage) {
       return new TransformingSetSupplier<I, Image>(new Supplier<Iterable<I>>() {
 
          @Override
@@ -139,7 +152,7 @@
    @Provides
    @Singleton
    protected CreateNodeWithGroupEncodedIntoName defineAddNodeWithTagStrategy(
-         AdaptingComputeServiceStrategies<N, H, I, L> in) {
+            AdaptingComputeServiceStrategies<N, H, I, L> in) {
       return in;
    }
 
diff --git a/compute/src/main/java/org/jclouds/compute/predicates/NodePresentAndInIntendedState.java b/compute/src/main/java/org/jclouds/compute/predicates/NodePresentAndInIntendedState.java
index dd79eaa..eb7139d 100644
--- a/compute/src/main/java/org/jclouds/compute/predicates/NodePresentAndInIntendedState.java
+++ b/compute/src/main/java/org/jclouds/compute/predicates/NodePresentAndInIntendedState.java
@@ -25,9 +25,9 @@
 import javax.annotation.Resource;
 import javax.inject.Singleton;
 
-import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
 import org.jclouds.logging.Logger;
 
 import com.google.common.base.Predicate;
@@ -43,18 +43,18 @@
 @Singleton
 public class NodePresentAndInIntendedState implements Predicate<NodeMetadata> {
 
-   private final ComputeService client;
+   private final GetNodeMetadataStrategy client;
    private final NodeState intended;
    private final Set<NodeState> invalids;
    @Resource
    protected Logger logger = Logger.NULL;
 
    @Inject
-   public NodePresentAndInIntendedState(NodeState intended, ComputeService client) {
+   public NodePresentAndInIntendedState(NodeState intended, GetNodeMetadataStrategy client) {
       this(intended, ImmutableSet.of(NodeState.ERROR), client);
    }
 
-   public NodePresentAndInIntendedState(NodeState intended, Set<NodeState> invalids, ComputeService client) {
+   public NodePresentAndInIntendedState(NodeState intended, Set<NodeState> invalids, GetNodeMetadataStrategy client) {
       this.intended = intended;
       this.client = client;
       this.invalids = invalids;
@@ -75,6 +75,6 @@
    private NodeMetadata refresh(NodeMetadata node) {
       if (node == null || node.getId() == null)
          return null;
-      return client.getNodeMetadata(node.getId());
+      return client.getNode(node.getId());
    }
 }
diff --git a/compute/src/main/java/org/jclouds/compute/predicates/NodeRunning.java b/compute/src/main/java/org/jclouds/compute/predicates/NodeRunning.java
index 0c03ce6..3811c7c 100644
--- a/compute/src/main/java/org/jclouds/compute/predicates/NodeRunning.java
+++ b/compute/src/main/java/org/jclouds/compute/predicates/NodeRunning.java
@@ -20,8 +20,8 @@
 
 import javax.inject.Singleton;
 
-import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Inject;
@@ -36,7 +36,7 @@
 public class NodeRunning extends NodePresentAndInIntendedState {
 
    @Inject
-   public NodeRunning(ComputeService client) {
+   public NodeRunning(GetNodeMetadataStrategy client) {
       super(NodeState.RUNNING, ImmutableSet.of(NodeState.ERROR, NodeState.TERMINATED), client);
    }
 }
diff --git a/compute/src/main/java/org/jclouds/compute/predicates/NodeSuspended.java b/compute/src/main/java/org/jclouds/compute/predicates/NodeSuspended.java
index f497453..f0dc578 100644
--- a/compute/src/main/java/org/jclouds/compute/predicates/NodeSuspended.java
+++ b/compute/src/main/java/org/jclouds/compute/predicates/NodeSuspended.java
@@ -20,8 +20,8 @@
 
 import javax.inject.Singleton;
 
-import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
 
 import com.google.inject.Inject;
 
@@ -35,7 +35,7 @@
 public class NodeSuspended extends NodePresentAndInIntendedState {
 
    @Inject
-   public NodeSuspended(ComputeService client) {
+   public NodeSuspended(GetNodeMetadataStrategy client) {
       super(NodeState.SUSPENDED, client);
    }
 }
diff --git a/compute/src/main/java/org/jclouds/compute/predicates/NodeTerminated.java b/compute/src/main/java/org/jclouds/compute/predicates/NodeTerminated.java
index 0cd90c4..b28bead 100644
--- a/compute/src/main/java/org/jclouds/compute/predicates/NodeTerminated.java
+++ b/compute/src/main/java/org/jclouds/compute/predicates/NodeTerminated.java
@@ -23,9 +23,9 @@
 import javax.annotation.Resource;
 import javax.inject.Singleton;
 
-import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
 import org.jclouds.logging.Logger;
 
 import com.google.common.base.Predicate;
@@ -40,13 +40,13 @@
 @Singleton
 public class NodeTerminated implements Predicate<NodeMetadata> {
 
-   private final ComputeService client;
+   private final GetNodeMetadataStrategy client;
 
    @Resource
    protected Logger logger = Logger.NULL;
 
    @Inject
-   public NodeTerminated(ComputeService client) {
+   public NodeTerminated(GetNodeMetadataStrategy client) {
       this.client = client;
    }
 
@@ -61,6 +61,6 @@
    }
 
    private NodeMetadata refresh(NodeMetadata node) {
-      return client.getNodeMetadata(node.getId());
+      return client.getNode(node.getId());
    }
 }
diff --git a/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java b/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java
index 649a00a..7ffb562 100644
--- a/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java
+++ b/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java
@@ -39,6 +39,24 @@
    
    public static final String PROPERTY_INIT_STATUS_INITIAL_PERIOD = "jclouds.compute.init-status.initial-period";
    public static final String PROPERTY_INIT_STATUS_MAX_PERIOD = "jclouds.compute.init-status.max-period";
+   
+   /**
+    * overrides the image specified in the subclass of {@link BaseComputeServiceContextModule#provideTemplate}
+    */
+   public static final String PROPERTY_IMAGE_ID = "jclouds.image-id";
+
+   /**
+    * username and, if colon delimited, password of the default user on the image that is or can become root
+    * <p/>
+    * ex. {@code ubuntu}
+    * ex. {@code toor:password}
+    */
+   public static final String PROPERTY_IMAGE_LOGIN_USER = "jclouds.image.login-user";
+
+   /**
+    * true if gaining a sudo shell requires a password
+    */
+   public static final String PROPERTY_IMAGE_AUTHENTICATE_SUDO = "jclouds.image.authenticate-sudo";
 
    /**
     * comma-separated nodes that we shouldn't attempt to list as they are dead
diff --git a/compute/src/main/java/org/jclouds/ssh/SshClient.java b/compute/src/main/java/org/jclouds/ssh/SshClient.java
index 5b2730b..894b69b 100644
--- a/compute/src/main/java/org/jclouds/ssh/SshClient.java
+++ b/compute/src/main/java/org/jclouds/ssh/SshClient.java
@@ -18,9 +18,6 @@
  */
 package org.jclouds.ssh;
 
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.domain.Credentials;
 import org.jclouds.domain.LoginCredentials;
@@ -56,10 +53,8 @@
 
    ExecResponse exec(String command);
 
-   @PostConstruct
    void connect();
 
-   @PreDestroy
    void disconnect();
 
    void put(String path, String contents);
diff --git a/compute/src/test/java/org/jclouds/compute/BaseVersionedServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseVersionedServiceLiveTest.java
index a01c053..c02573e 100644
--- a/compute/src/test/java/org/jclouds/compute/BaseVersionedServiceLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/BaseVersionedServiceLiveTest.java
@@ -18,15 +18,11 @@
  */
 package org.jclouds.compute;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Strings.emptyToNull;
-
 import java.util.Properties;
 
-import org.jclouds.Constants;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.domain.LoginCredentials.Builder;
-import org.jclouds.rest.RestContextFactory;
+import org.jclouds.rest.BaseRestClientLiveTest;
 import org.testng.annotations.BeforeClass;
 
 import com.google.common.base.Splitter;
@@ -36,42 +32,16 @@
  * 
  * @author Jason King
  */
-public abstract class BaseVersionedServiceLiveTest {
-   protected String prefix = System.getProperty("user.name");
+public abstract class BaseVersionedServiceLiveTest extends BaseRestClientLiveTest {
 
-   protected String provider;
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    protected String imageId;
    protected String loginUser;
    protected String authenticateSudo;
    protected LoginCredentials loginCredentials = LoginCredentials.builder().user("root").build();
 
-   protected Properties setupRestProperties() {
-      return RestContextFactory.getPropertiesFromResource("/rest.properties");
-   }
-
+   @Override
    protected Properties setupProperties() {
-
-      if (emptyToNull(provider) == null)
-         throw new NullPointerException("provider must not be null or empty:" + provider);
-      if (emptyToNull(identity) == null)
-         throw new NullPointerException("identity must not be null or empty:" + provider);
-
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-
-      overrides.setProperty(provider + ".identity", identity);
-
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
+      Properties overrides= super.setupProperties();   
       if (imageId != null)
          overrides.setProperty(provider + ".image-id", imageId);
       if (loginUser != null)
@@ -84,10 +54,7 @@
 
    @BeforeClass
    protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+      super.setupCredentials();
       imageId = System.getProperty("test." + provider + ".image-id");
       loginUser = System.getProperty("test." + provider + ".image.login-user");
       authenticateSudo = System.getProperty("test." + provider + ".image.authenticate-sudo");
diff --git a/compute/src/test/java/org/jclouds/compute/ComputeServiceContextFactoryTest.java b/compute/src/test/java/org/jclouds/compute/ComputeServiceContextFactoryTest.java
index fcc5ad3..af977d5 100644
--- a/compute/src/test/java/org/jclouds/compute/ComputeServiceContextFactoryTest.java
+++ b/compute/src/test/java/org/jclouds/compute/ComputeServiceContextFactoryTest.java
@@ -43,7 +43,7 @@
       @SuppressWarnings("rawtypes")
       ComputeServiceContext context = new ComputeServiceContextFactory()
                .createContext(new StandaloneComputeServiceContextSpec<ConcurrentMap, NodeMetadata, Hardware, Image, Location>(
-                        "stub", "stub", "1", "", "identity", "credential", ConcurrentMap.class,
+                        "stub", "stub", "1", "", "", "identity", "credential", ConcurrentMap.class,
                         StubComputeServiceContextBuilder.class, ImmutableSet.<Module> of()));
 
       context.getComputeService().listNodes();
diff --git a/compute/src/test/java/org/jclouds/compute/predicates/NodePredicatesTest.java b/compute/src/test/java/org/jclouds/compute/predicates/NodePredicatesTest.java
index 0abfcd9..4d55df7 100644
--- a/compute/src/test/java/org/jclouds/compute/predicates/NodePredicatesTest.java
+++ b/compute/src/test/java/org/jclouds/compute/predicates/NodePredicatesTest.java
@@ -22,9 +22,9 @@
 import static org.easymock.classextension.EasyMock.createMock;
 import static org.easymock.classextension.EasyMock.replay;
 
-import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -38,15 +38,15 @@
 public class NodePredicatesTest {
 
    private NodeMetadata node;
-   private ComputeService computeService;
+   private GetNodeMetadataStrategy computeService;
 
    @BeforeMethod
    public void setUp() throws Exception {
       node = createMock(NodeMetadata.class);
-      computeService = createMock(ComputeService.class);
+      computeService = createMock(GetNodeMetadataStrategy.class);
 
       expect(node.getId()).andReturn("myid").anyTimes();
-      expect(computeService.getNodeMetadata("myid")).andReturn(node).anyTimes();
+      expect(computeService.getNode("myid")).andReturn(node).anyTimes();
       expect(node.getLocation()).andReturn(null).anyTimes();
    }
 
diff --git a/core/pom.xml b/core/pom.xml
index e395f78..ebb06b6 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -142,8 +142,7 @@
                 <configuration>
                     <instructions>
                         <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
-                        <Export-Package>com.google.gson;-split-package:=merge-first,
-                                        org.jclouds.*;version=${project.version}</Export-Package>
+                        <Export-Package>org.jclouds.*;version=${project.version}</Export-Package>
                         <DynamicImport-Package>org.jclouds.*</DynamicImport-Package>
                     </instructions>
                 </configuration>
diff --git a/core/src/main/java/com/google/gson/internal/Streams.java b/core/src/main/java/com/google/gson/internal/Streams.java
deleted file mode 100644
index 0459793..0000000
--- a/core/src/main/java/com/google/gson/internal/Streams.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed 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 com.google.gson.internal;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonIOException;
-import com.google.gson.JsonNull;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonSyntaxException;
-import com.google.gson.internal.bind.TypeAdapters;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
-import com.google.gson.stream.MalformedJsonException;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.Writer;
-
-import org.jclouds.json.internal.JsonLiteral;
-
-/**
- * Reads and writes GSON parse trees over streams.
- */
-public final class Streams {
-  /**
-   * Takes a reader in any state and returns the next value as a JsonElement.
-   */
-  public static JsonElement parse(JsonReader reader) throws JsonParseException {
-    boolean isEmpty = true;
-    try {
-      reader.peek();
-      isEmpty = false;
-      return TypeAdapters.JSON_ELEMENT.read(reader);
-    } catch (EOFException e) {
-      /*
-       * For compatibility with JSON 1.5 and earlier, we return a JsonNull for
-       * empty documents instead of throwing.
-       */
-      if (isEmpty) {
-        return JsonNull.INSTANCE;
-      }
-      throw new JsonIOException(e);
-    } catch (MalformedJsonException e) {
-      throw new JsonSyntaxException(e);
-    } catch (IOException e) {
-      throw new JsonIOException(e);
-    } catch (NumberFormatException e) {
-      throw new JsonSyntaxException(e);
-    }
-  }
-
-  /**
-   * Writes the JSON element to the writer, recursively.
-   */
-  public static void write(JsonElement element, JsonWriter writer) throws IOException {
-     //BEGIN JCLOUDS PATCH
-     // * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
-     if (element instanceof JsonLiteral ) {
-        writer.value(JsonLiteral.class.cast(element));
-        //END JCLOUDS PATCH
-     } else {
-        TypeAdapters.JSON_ELEMENT.write(writer, element);
-     }
-  }
-
-  public static Writer writerForAppendable(Appendable appendable) {
-    return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
-  }
-
-  /**
-   * Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
-   * is used.
-   */
-  private static class AppendableWriter extends Writer {
-    private final Appendable appendable;
-    private final CurrentWrite currentWrite = new CurrentWrite();
-
-    private AppendableWriter(Appendable appendable) {
-      this.appendable = appendable;
-    }
-
-    @Override public void write(char[] chars, int offset, int length) throws IOException {
-      currentWrite.chars = chars;
-      appendable.append(currentWrite, offset, offset + length);
-    }
-
-    @Override public void write(int i) throws IOException {
-      appendable.append((char) i);
-    }
-
-    @Override public void flush() {}
-    @Override public void close() {}
-
-    /**
-     * A mutable char sequence pointing at a single char[].
-     */
-    static class CurrentWrite implements CharSequence {
-      char[] chars;
-      public int length() {
-        return chars.length;
-      }
-      public char charAt(int i) {
-        return chars[i];
-      }
-      public CharSequence subSequence(int start, int end) {
-        return new String(chars, start, end - start);
-      }
-    }
-  }
-
-}
diff --git a/core/src/main/java/com/google/gson/stream/JsonWriter.java b/core/src/main/java/com/google/gson/stream/JsonWriter.java
deleted file mode 100644
index c5996e9..0000000
--- a/core/src/main/java/com/google/gson/stream/JsonWriter.java
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * Licensed 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 com.google.gson.stream;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.jclouds.json.internal.JsonLiteral;
-
-
-/**
- * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
- * encoded value to a stream, one token at a time. The stream includes both
- * literal values (strings, numbers, booleans and nulls) as well as the begin
- * and end delimiters of objects and arrays.
- *
- * <h3>Encoding JSON</h3>
- * To encode your data as JSON, create a new {@code JsonWriter}. Each JSON
- * document must contain one top-level array or object. Call methods on the
- * writer as you walk the structure's contents, nesting arrays and objects as
- * necessary:
- * <ul>
- *   <li>To write <strong>arrays</strong>, first call {@link #beginArray()}.
- *       Write each of the array's elements with the appropriate {@link #value}
- *       methods or by nesting other arrays and objects. Finally close the array
- *       using {@link #endArray()}.
- *   <li>To write <strong>objects</strong>, first call {@link #beginObject()}.
- *       Write each of the object's properties by alternating calls to
- *       {@link #name} with the property's value. Write property values with the
- *       appropriate {@link #value} method or by nesting other objects or arrays.
- *       Finally close the object using {@link #endObject()}.
- * </ul>
- *
- * <h3>Example</h3>
- * Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
- * [
- *   {
- *     "id": 912345678901,
- *     "text": "How do I stream JSON in Java?",
- *     "geo": null,
- *     "user": {
- *       "name": "json_newb",
- *       "followers_count": 41
- *      }
- *   },
- *   {
- *     "id": 912345678902,
- *     "text": "@json_newb just use JsonWriter!",
- *     "geo": [50.454722, -104.606667],
- *     "user": {
- *       "name": "jesse",
- *       "followers_count": 2
- *     }
- *   }
- * ]}</pre>
- * This code encodes the above structure: <pre>   {@code
- *   public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
- *     JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
- *     writer.setIndentSpaces(4);
- *     writeMessagesArray(writer, messages);
- *     writer.close();
- *   }
- *
- *   public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
- *     writer.beginArray();
- *     for (Message message : messages) {
- *       writeMessage(writer, message);
- *     }
- *     writer.endArray();
- *   }
- *
- *   public void writeMessage(JsonWriter writer, Message message) throws IOException {
- *     writer.beginObject();
- *     writer.name("id").value(message.getId());
- *     writer.name("text").value(message.getText());
- *     if (message.getGeo() != null) {
- *       writer.name("geo");
- *       writeDoublesArray(writer, message.getGeo());
- *     } else {
- *       writer.name("geo").nullValue();
- *     }
- *     writer.name("user");
- *     writeUser(writer, message.getUser());
- *     writer.endObject();
- *   }
- *
- *   public void writeUser(JsonWriter writer, User user) throws IOException {
- *     writer.beginObject();
- *     writer.name("name").value(user.getName());
- *     writer.name("followers_count").value(user.getFollowersCount());
- *     writer.endObject();
- *   }
- *
- *   public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
- *     writer.beginArray();
- *     for (Double value : doubles) {
- *       writer.value(value);
- *     }
- *     writer.endArray();
- *   }}</pre>
- *
- * <p>Each {@code JsonWriter} may be used to write a single JSON stream.
- * Instances of this class are not thread safe. Calls that would result in a
- * malformed JSON string will fail with an {@link IllegalStateException}.
- *
- * @author Jesse Wilson
- * @since 1.6
- */
-public class JsonWriter implements Closeable {
-
-  /** The output data, containing at most one top-level array or object. */
-  private final Writer out;
-
-  private final List<JsonScope> stack = new ArrayList<JsonScope>();
-  {
-    stack.add(JsonScope.EMPTY_DOCUMENT);
-  }
-
-  /**
-   * A string containing a full set of spaces for a single level of
-   * indentation, or null for no pretty printing.
-   */
-  private String indent;
-
-  /**
-   * The name/value separator; either ":" or ": ".
-   */
-  private String separator = ":";
-
-  private boolean lenient;
-
-  private boolean htmlSafe;
-
-  private String deferredName;
-
-  private boolean serializeNulls = true;
-
-  /**
-   * Creates a new instance that writes a JSON-encoded stream to {@code out}.
-   * For best performance, ensure {@link Writer} is buffered; wrapping in
-   * {@link java.io.BufferedWriter BufferedWriter} if necessary.
-   */
-  public JsonWriter(Writer out) {
-    if (out == null) {
-      throw new NullPointerException("out == null");
-    }
-    this.out = out;
-  }
-
-  /**
-   * Sets the indentation string to be repeated for each level of indentation
-   * in the encoded document. If {@code indent.isEmpty()} the encoded document
-   * will be compact. Otherwise the encoded document will be more
-   * human-readable.
-   *
-   * @param indent a string containing only whitespace.
-   */
-  public final void setIndent(String indent) {
-    if (indent.length() == 0) {
-      this.indent = null;
-      this.separator = ":";
-    } else {
-      this.indent = indent;
-      this.separator = ": ";
-    }
-  }
-
-  /**
-   * Configure this writer to relax its syntax rules. By default, this writer
-   * only emits well-formed JSON as specified by <a
-   * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer
-   * to lenient permits the following:
-   * <ul>
-   *   <li>Top-level values of any type. With strict writing, the top-level
-   *       value must be an object or an array.
-   *   <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
-   *       Double#isInfinite() infinities}.
-   * </ul>
-   */
-  public final void setLenient(boolean lenient) {
-    this.lenient = lenient;
-  }
-
-  /**
-   * Returns true if this writer has relaxed syntax rules.
-   */
-  public boolean isLenient() {
-    return lenient;
-  }
-
-  /**
-   * Configure this writer to emit JSON that's safe for direct inclusion in HTML
-   * and XML documents. This escapes the HTML characters {@code <}, {@code >},
-   * {@code &} and {@code =} before writing them to the stream. Without this
-   * setting, your XML/HTML encoder should replace these characters with the
-   * corresponding escape sequences.
-   */
-  public final void setHtmlSafe(boolean htmlSafe) {
-    this.htmlSafe = htmlSafe;
-  }
-
-  /**
-   * Returns true if this writer writes JSON that's safe for inclusion in HTML
-   * and XML documents.
-   */
-  public final boolean isHtmlSafe() {
-    return htmlSafe;
-  }
-
-  /**
-   * Sets whether object members are serialized when their value is null.
-   * This has no impact on array elements. The default is true.
-   */
-  public final void setSerializeNulls(boolean serializeNulls) {
-    this.serializeNulls = serializeNulls;
-  }
-
-  /**
-   * Returns true if object members are serialized when their value is null.
-   * This has no impact on array elements. The default is true.
-   */
-  public final boolean getSerializeNulls() {
-    return serializeNulls;
-  }
-
-  /**
-   * Begins encoding a new array. Each call to this method must be paired with
-   * a call to {@link #endArray}.
-   *
-   * @return this writer.
-   */
-  public JsonWriter beginArray() throws IOException {
-    writeDeferredName();
-    return open(JsonScope.EMPTY_ARRAY, "[");
-  }
-
-  /**
-   * Ends encoding the current array.
-   *
-   * @return this writer.
-   */
-  public JsonWriter endArray() throws IOException {
-    return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]");
-  }
-
-  /**
-   * Begins encoding a new object. Each call to this method must be paired
-   * with a call to {@link #endObject}.
-   *
-   * @return this writer.
-   */
-  public JsonWriter beginObject() throws IOException {
-    writeDeferredName();
-    return open(JsonScope.EMPTY_OBJECT, "{");
-  }
-
-  /**
-   * Ends encoding the current object.
-   *
-   * @return this writer.
-   */
-  public JsonWriter endObject() throws IOException {
-    return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}");
-  }
-
-  /**
-   * Enters a new scope by appending any necessary whitespace and the given
-   * bracket.
-   */
-  private JsonWriter open(JsonScope empty, String openBracket) throws IOException {
-    beforeValue(true);
-    stack.add(empty);
-    out.write(openBracket);
-    return this;
-  }
-
-  /**
-   * Closes the current scope by appending any necessary whitespace and the
-   * given bracket.
-   */
-  private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket)
-      throws IOException {
-    JsonScope context = peek();
-    if (context != nonempty && context != empty) {
-      throw new IllegalStateException("Nesting problem: " + stack);
-    }
-    if (deferredName != null) {
-      throw new IllegalStateException("Dangling name: " + deferredName);
-    }
-
-    stack.remove(stack.size() - 1);
-    if (context == nonempty) {
-      newline();
-    }
-    out.write(closeBracket);
-    return this;
-  }
-
-  /**
-   * Returns the value on the top of the stack.
-   */
-  private JsonScope peek() {
-    return stack.get(stack.size() - 1);
-  }
-
-  /**
-   * Replace the value on the top of the stack with the given value.
-   */
-  private void replaceTop(JsonScope topOfStack) {
-    stack.set(stack.size() - 1, topOfStack);
-  }
-
-  /**
-   * Encodes the property name.
-   *
-   * @param name the name of the forthcoming value. May not be null.
-   * @return this writer.
-   */
-  public JsonWriter name(String name) throws IOException {
-    if (name == null) {
-      throw new NullPointerException("name == null");
-    }
-    if (deferredName != null) {
-      throw new IllegalStateException();
-    }
-    deferredName = name;
-    return this;
-  }
-
-  private void writeDeferredName() throws IOException {
-    if (deferredName != null) {
-      beforeName();
-      string(deferredName);
-      deferredName = null;
-    }
-  }
-
-  /**
-   * Encodes {@code value}.
-   *
-   * @param value the literal string value, or null to encode a null literal.
-   * @return this writer.
-   */
-  public JsonWriter value(String value) throws IOException {
-    if (value == null) {
-      return nullValue();
-    }
-    writeDeferredName();
-    beforeValue(false);
-    string(value);
-    return this;
-  }
-
-  /**
-   * Encodes {@code null}.
-   *
-   * @return this writer.
-   */
-  public JsonWriter nullValue() throws IOException {
-    if (deferredName != null) {
-      if (serializeNulls) {
-        writeDeferredName();
-      } else {
-        deferredName = null;
-        return this; // skip the name and the value
-      }
-    }
-    beforeValue(false);
-    out.write("null");
-    return this;
-  }
-//BEGIN JCLOUDS PATCH
-//* @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
-  /**
-   * Writes {@code value} literally
-   * 
-   * @return this writer.
-   */
-  public JsonWriter value(JsonLiteral value) throws IOException {
-     if (value == null) {
-        return nullValue();
-     }
-     writeDeferredName();
-     beforeValue(false);
-     out.write(value.toString());
-     return this;
-  }
-//END JCLOUDS PATCH
-  /**
-   * Encodes {@code value}.
-   *
-   * @return this writer.
-   */
-  public JsonWriter value(boolean value) throws IOException {
-    writeDeferredName();
-    beforeValue(false);
-    out.write(value ? "true" : "false");
-    return this;
-  }
-
-  /**
-   * Encodes {@code value}.
-   *
-   * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
-   *     {@link Double#isInfinite() infinities}.
-   * @return this writer.
-   */
-  public JsonWriter value(double value) throws IOException {
-    if (Double.isNaN(value) || Double.isInfinite(value)) {
-      throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
-    }
-    writeDeferredName();
-    beforeValue(false);
-    out.append(Double.toString(value));
-    return this;
-  }
-
-  /**
-   * Encodes {@code value}.
-   *
-   * @return this writer.
-   */
-  public JsonWriter value(long value) throws IOException {
-    writeDeferredName();
-    beforeValue(false);
-    out.write(Long.toString(value));
-    return this;
-  }
-
-  /**
-   * Encodes {@code value}.
-   *
-   * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
-   *     {@link Double#isInfinite() infinities}.
-   * @return this writer.
-   */
-  public JsonWriter value(Number value) throws IOException {
-    if (value == null) {
-      return nullValue();
-    }
-
-    writeDeferredName();
-    String string = value.toString();
-    if (!lenient
-        && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
-      throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
-    }
-    beforeValue(false);
-    out.append(string);
-    return this;
-  }
-
-  /**
-   * Ensures all buffered data is written to the underlying {@link Writer}
-   * and flushes that writer.
-   */
-  public void flush() throws IOException {
-    out.flush();
-  }
-
-  /**
-   * Flushes and closes this writer and the underlying {@link Writer}.
-   *
-   * @throws IOException if the JSON document is incomplete.
-   */
-  public void close() throws IOException {
-    out.close();
-
-    if (peek() != JsonScope.NONEMPTY_DOCUMENT) {
-      throw new IOException("Incomplete document");
-    }
-  }
-
-  private void string(String value) throws IOException {
-    out.write("\"");
-    for (int i = 0, length = value.length(); i < length; i++) {
-      char c = value.charAt(i);
-
-      /*
-       * From RFC 4627, "All Unicode characters may be placed within the
-       * quotation marks except for the characters that must be escaped:
-       * quotation mark, reverse solidus, and the control characters
-       * (U+0000 through U+001F)."
-       *
-       * We also escape '\u2028' and '\u2029', which JavaScript interprets as
-       * newline characters. This prevents eval() from failing with a syntax
-       * error. http://code.google.com/p/google-gson/issues/detail?id=341
-       */
-      switch (c) {
-      case '"':
-      case '\\':
-        out.write('\\');
-        out.write(c);
-        break;
-
-      case '\t':
-        out.write("\\t");
-        break;
-
-      case '\b':
-        out.write("\\b");
-        break;
-
-      case '\n':
-        out.write("\\n");
-        break;
-
-      case '\r':
-        out.write("\\r");
-        break;
-
-      case '\f':
-        out.write("\\f");
-        break;
-
-      case '<':
-      case '>':
-      case '&':
-      case '=':
-      case '\'':
-        if (htmlSafe) {
-          out.write(String.format("\\u%04x", (int) c));
-        } else {
-          out.write(c);
-        }
-        break;
-
-      case '\u2028':
-      case '\u2029':
-        out.write(String.format("\\u%04x", (int) c));
-        break;
-
-      default:
-        if (c <= 0x1F) {
-          out.write(String.format("\\u%04x", (int) c));
-        } else {
-          out.write(c);
-        }
-        break;
-      }
-    }
-    out.write("\"");
-  }
-
-  private void newline() throws IOException {
-    if (indent == null) {
-      return;
-    }
-
-    out.write("\n");
-    for (int i = 1; i < stack.size(); i++) {
-      out.write(indent);
-    }
-  }
-
-  /**
-   * Inserts any necessary separators and whitespace before a name. Also
-   * adjusts the stack to expect the name's value.
-   */
-  private void beforeName() throws IOException {
-    JsonScope context = peek();
-    if (context == JsonScope.NONEMPTY_OBJECT) { // first in object
-      out.write(',');
-    } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object!
-      throw new IllegalStateException("Nesting problem: " + stack);
-    }
-    newline();
-    replaceTop(JsonScope.DANGLING_NAME);
-  }
-
-  /**
-   * Inserts any necessary separators and whitespace before a literal value,
-   * inline array, or inline object. Also adjusts the stack to expect either a
-   * closing bracket or another element.
-   *
-   * @param root true if the value is a new array or object, the two values
-   *     permitted as top-level elements.
-   */
-  private void beforeValue(boolean root) throws IOException {
-    switch (peek()) {
-    case EMPTY_DOCUMENT: // first in document
-      if (!lenient && !root) {
-        throw new IllegalStateException(
-            "JSON must start with an array or an object.");
-      }
-      replaceTop(JsonScope.NONEMPTY_DOCUMENT);
-      break;
-
-    case EMPTY_ARRAY: // first in array
-      replaceTop(JsonScope.NONEMPTY_ARRAY);
-      newline();
-      break;
-
-    case NONEMPTY_ARRAY: // another in array
-      out.append(',');
-      newline();
-      break;
-
-    case DANGLING_NAME: // value for name
-      out.append(separator);
-      replaceTop(JsonScope.NONEMPTY_OBJECT);
-      break;
-
-    case NONEMPTY_DOCUMENT:
-        throw new IllegalStateException(
-            "JSON must have only one top-level value.");
-
-    default:
-      throw new IllegalStateException("Nesting problem: " + stack);
-    }
-  }
-}
diff --git a/core/src/main/java/org/jclouds/Constants.java b/core/src/main/java/org/jclouds/Constants.java
index 2052da0..cc81d4d 100644
--- a/core/src/main/java/org/jclouds/Constants.java
+++ b/core/src/main/java/org/jclouds/Constants.java
@@ -191,7 +191,7 @@
    public static final String PROPERTY_API = "jclouds.api";
 
    /**
-    * String property.
+    * String property. default empty string
     * <p/>
     * Explicitly identifies the version of an api.
     */
@@ -200,6 +200,17 @@
    /**
     * String property.
     * <p/>
+    * Explicitly identifies the build that the server jclouds connects to is running.
+    * 
+    * For example, for virtualbox, the api version may be {@code 4.1.8} while the build version is
+    * {@code 4.1.8r75467}. Or a vcloud endpoint may be api version {@code 1.0} while the build is
+    * {@code 1.5.0.0.124312}
+    */
+   public static final String PROPERTY_BUILD_VERSION = "jclouds.build-version";
+   
+   /**
+    * String property.
+    * <p/>
     * Explicitly identifies the most top-level endpoint to a service provider. This helps
     * differentiate two providers of the same api, or a different environments providing the same
     * api.
diff --git a/core/src/main/java/org/jclouds/JcloudsVersion.java b/core/src/main/java/org/jclouds/JcloudsVersion.java
new file mode 100644
index 0000000..ec36b1f
--- /dev/null
+++ b/core/src/main/java/org/jclouds/JcloudsVersion.java
@@ -0,0 +1,106 @@
+/**
+ * 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * @author Andrew Phillips
+ */
+public class JcloudsVersion {
+    @VisibleForTesting
+    static final String VERSION_RESOURCE_FILE = "META-INF/maven/org.jclouds/jclouds-core/pom.properties";
+    private static final String VERSION_PROPERTY_NAME = "version";
+
+    // TODO: stop supporting x.y.z-rc-n after the 1.3.0 release
+    // x.y.z or x.y.z-rc.n or x.y.z-rc-n, optionally with -SNAPSHOT suffix - see http://semver.org
+    private static final Pattern SEMANTIC_VERSION_PATTERN =
+        Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(?:-rc[-\\.](\\d+))?(?:-SNAPSHOT)?");
+
+    private static final JcloudsVersion INSTANCE = new JcloudsVersion();
+
+    public final int majorVersion;
+    public final int minorVersion;
+    public final int patchVersion;
+    public final boolean releaseCandidate;
+    private final String version;
+
+    /**
+     * Non-null iff {@link #releaseCandidate} is {@code true}
+     */
+    public final @Nullable Integer releaseCandidateVersion;
+    public final boolean snapshot;
+
+    @VisibleForTesting
+    JcloudsVersion() {
+        this(readVersionPropertyFromClasspath());
+    }
+
+    private static String readVersionPropertyFromClasspath() {
+        Properties versionProperties = new Properties();
+        try {
+            versionProperties.load(checkNotNull(Thread.currentThread().getContextClassLoader().getResourceAsStream(VERSION_RESOURCE_FILE), VERSION_RESOURCE_FILE));
+        } catch (IOException exception) {
+            throw new IllegalStateException(format("Unable to load version resource file '%s'", VERSION_RESOURCE_FILE), exception);
+        }
+        return checkNotNull(versionProperties.getProperty(VERSION_PROPERTY_NAME), VERSION_PROPERTY_NAME);
+    }
+
+    @VisibleForTesting
+    JcloudsVersion(String version) {
+        Matcher versionMatcher = SEMANTIC_VERSION_PATTERN.matcher(version);
+        checkArgument(versionMatcher.matches(), "Version '%s' did not match expected pattern '%s'", 
+                version, SEMANTIC_VERSION_PATTERN);
+        this.version = version;
+        // a match will produce three or four matching groups (release candidate version optional)
+        majorVersion = Integer.valueOf(versionMatcher.group(1));
+        minorVersion = Integer.valueOf(versionMatcher.group(2));
+        patchVersion = Integer.valueOf(versionMatcher.group(3));
+        String releaseCandidateVersionIfPresent = versionMatcher.group(4);
+        if (releaseCandidateVersionIfPresent != null) {
+            releaseCandidate = true;
+            releaseCandidateVersion = Integer.valueOf(releaseCandidateVersionIfPresent);
+        } else {
+            releaseCandidate = false;
+            releaseCandidateVersion = null;
+        }
+        // endsWith("T") would be cheaper but we only do this once...
+        snapshot = version.endsWith("-SNAPSHOT");
+    }
+
+    @Override
+    public String toString() {
+        return version;
+    }
+
+    public static JcloudsVersion get() {
+        return INSTANCE;
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/PropertiesBuilder.java b/core/src/main/java/org/jclouds/PropertiesBuilder.java
index 3029a68..f79a1cd 100644
--- a/core/src/main/java/org/jclouds/PropertiesBuilder.java
+++ b/core/src/main/java/org/jclouds/PropertiesBuilder.java
@@ -19,6 +19,7 @@
 package org.jclouds;
 
 import static org.jclouds.Constants.PROPERTY_API_VERSION;
+import static org.jclouds.Constants.PROPERTY_BUILD_VERSION;
 import static org.jclouds.Constants.PROPERTY_CONNECTION_TIMEOUT;
 import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
 import static org.jclouds.Constants.PROPERTY_ENDPOINT;
@@ -218,6 +219,7 @@
       props.setProperty(PROPERTY_CONNECTION_TIMEOUT, 60000 + "");
       props.setProperty(PROPERTY_IO_WORKER_THREADS, 20 + "");
       props.setProperty(PROPERTY_USER_THREADS, 0 + "");
+      props.setProperty(PROPERTY_BUILD_VERSION, "");
       props.setProperty(PROPERTY_MAX_CONNECTION_REUSE, 75 + "");
       props.setProperty(PROPERTY_MAX_SESSION_FAILURES, 2 + "");
       props.setProperty(PROPERTY_SESSION_INTERVAL, 60 + "");
@@ -249,6 +251,11 @@
       return this;
    }
 
+   public PropertiesBuilder buildVersion(String buildVersion) {
+      properties.setProperty(PROPERTY_BUILD_VERSION, buildVersion);
+      return this;
+   }
+
    public PropertiesBuilder credentials(String identity, @Nullable String credential) {
       properties.setProperty(PROPERTY_IDENTITY, identity);
       if (credential != null)
diff --git a/core/src/main/java/org/jclouds/crypto/Pems.java b/core/src/main/java/org/jclouds/crypto/Pems.java
index 76b8bb6..55740ed 100644
--- a/core/src/main/java/org/jclouds/crypto/Pems.java
+++ b/core/src/main/java/org/jclouds/crypto/Pems.java
@@ -42,11 +42,11 @@
 
 import net.oauth.signature.pem.PEMReader;
 import net.oauth.signature.pem.PKCS1EncodedKeySpec;
-import net.oauth.signature.pem.PKCS1EncodedPublicKeySpec;
 
 import org.bouncycastle.asn1.ASN1OutputStream;
 import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
 import org.jclouds.crypto.Pems.PemProcessor.ResultParser;
+import org.jclouds.crypto.pem.PKCS1EncodedPublicKeySpec;
 import org.jclouds.io.InputSuppliers;
 
 import com.google.common.annotations.Beta;
diff --git a/core/src/main/java/net/oauth/signature/pem/PKCS1EncodedPublicKeySpec.java b/core/src/main/java/org/jclouds/crypto/pem/PKCS1EncodedPublicKeySpec.java
similarity index 68%
rename from core/src/main/java/net/oauth/signature/pem/PKCS1EncodedPublicKeySpec.java
rename to core/src/main/java/org/jclouds/crypto/pem/PKCS1EncodedPublicKeySpec.java
index 3b25774..a075378 100644
--- a/core/src/main/java/net/oauth/signature/pem/PKCS1EncodedPublicKeySpec.java
+++ b/core/src/main/java/org/jclouds/crypto/pem/PKCS1EncodedPublicKeySpec.java
@@ -41,15 +41,15 @@
  *
  ****************************************************************************/
 
-package net.oauth.signature.pem;
+package org.jclouds.crypto.pem;
 
 import java.io.IOException;
-import java.math.BigInteger;
 import java.security.spec.RSAPublicKeySpec;
 
+import net.oauth.signature.pem.PKCS1EncodedKeySpec;
+
 /**
- * PKCS#1 encoded public key spec. In oauth package as they made all classes
- * package visible.
+ * PKCS#1 encoded public key spec.
  * 
  * 
  * @author Adrian Cole
@@ -79,41 +79,12 @@
    }
 
    /**
-    * Decode PKCS#1 encoded private key into RSAPublicKeySpec.
-    * 
-    * <p/>
-    * The ASN.1 syntax for the private key with CRT is
-    * 
-    * <pre>
-    * -- 
-    * -- Representation of RSA private key with information for the CRT algorithm.
-    * --
-    * RSAPrivateKey ::= SEQUENCE {
-    *   version           Version, 
-    *   modulus           INTEGER,  -- n
-    *   publicExponent    INTEGER,  -- e
-    * }
-    * </pre>
-    * 
-    * @param keyBytes
-    *           PKCS#1 encoded key
-    * @throws IOException
+    * get the modulus and public exponent by reusing {@link PKCS1EncodedKeySpec}
     */
-
    private void decode(byte[] keyBytes) throws IOException {
+      PKCS1EncodedKeySpec privateSpec = new PKCS1EncodedKeySpec(keyBytes);
 
-      DerParser parser = new DerParser(keyBytes);
-
-      Asn1Object sequence = parser.read();
-      if (sequence.getType() != DerParser.SEQUENCE)
-         throw new IOException("Invalid DER: not a sequence"); //$NON-NLS-1$
-
-      // Parse inside the sequence
-      parser = sequence.getParser();
-
-      BigInteger modulus = parser.read().getInteger();
-      BigInteger publicExp = parser.read().getInteger();
-
-      keySpec = new RSAPublicKeySpec(modulus, publicExp);
+      keySpec = new RSAPublicKeySpec(privateSpec.getKeySpec().getModulus(), privateSpec.getKeySpec()
+               .getPublicExponent());
    }
 }
diff --git a/core/src/main/java/org/jclouds/domain/JsonBall.java b/core/src/main/java/org/jclouds/domain/JsonBall.java
index b8fa6fb..42c7a1a 100644
--- a/core/src/main/java/org/jclouds/domain/JsonBall.java
+++ b/core/src/main/java/org/jclouds/domain/JsonBall.java
@@ -75,13 +75,18 @@
    public JsonBall(long value) {
       this.value = value + "";
    }
-
-   public JsonBall(String value) {
-      this.value = quoteStringIfNotNumber(checkNotNull(value, "value"));
+   
+   public JsonBall(boolean value) {
+      this.value = value + "";
    }
 
-   static String quoteStringIfNotNumber(String in) {
-      if (Patterns.JSON_STRING_PATTERN.matcher(in).find() && !Patterns.JSON_NUMBER_PATTERN.matcher(in).find()) {
+   public JsonBall(String value) {
+      this.value = quoteStringIfNotNumberOrBoolean(checkNotNull(value, "value"));
+   }
+   
+   static String quoteStringIfNotNumberOrBoolean(String in) {
+      if (Patterns.JSON_STRING_PATTERN.matcher(in).find() && !Patterns.JSON_NUMBER_PATTERN.matcher(in).find()
+               && !Patterns.JSON_BOOLEAN_PATTERN.matcher(in).find()) {
          return "\"" + in + "\"";
       }
       return in;
diff --git a/core/src/main/java/org/jclouds/http/HttpRequest.java b/core/src/main/java/org/jclouds/http/HttpRequest.java
index 340ab88..67c7ab7 100644
--- a/core/src/main/java/org/jclouds/http/HttpRequest.java
+++ b/core/src/main/java/org/jclouds/http/HttpRequest.java
@@ -22,13 +22,12 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.net.URI;
-import java.util.Arrays;
 import java.util.List;
 
+import org.jclouds.io.Payload;
 import org.jclouds.javax.annotation.Nullable;
 
-import org.jclouds.io.Payload;
-
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Multimap;
@@ -191,55 +190,26 @@
    public Builder toBuilder() {
       return Builder.from(this);
    }
-
+   
    @Override
    public int hashCode() {
-      final int prime = 31;
-      int result = super.hashCode();
-      result = prime * result + ((endpoint == null) ? 0 : endpoint.hashCode());
-      result = prime * result + ((method == null) ? 0 : method.hashCode());
-      result = prime * result + ((payload == null) ? 0 : payload.hashCode());
-      result = prime * result + ((headers == null) ? 0 : headers.hashCode());
-      result = prime * result + ((requestFilters == null) ? 0 : requestFilters.hashCode());
-      result = prime * result + Arrays.hashCode(skips);
-      return result;
+      return Objects.hashCode(method, endpoint, headers, payload);
    }
 
    @Override
    public boolean equals(Object obj) {
       if (this == obj)
          return true;
-      if (!super.equals(obj))
-         return false;
-      if (getClass() != obj.getClass())
+      if (!(obj instanceof HttpRequest))
          return false;
       HttpRequest other = (HttpRequest) obj;
-      if (endpoint == null) {
-         if (other.endpoint != null)
-            return false;
-      } else if (!endpoint.equals(other.endpoint))
+      if (!Objects.equal(method, other.method))
          return false;
-      if (method == null) {
-         if (other.method != null)
-            return false;
-      } else if (!method.equals(other.method))
+      if (!Objects.equal(endpoint, other.endpoint))
          return false;
-      if (payload == null) {
-         if (other.payload != null)
-            return false;
-      } else if (!payload.equals(other.payload))
+      if (!Objects.equal(headers, other.headers))
          return false;
-      if (headers == null) {
-         if (other.headers != null)
-            return false;
-      } else if (!headers.equals(other.headers))
-         return false;
-      if (requestFilters == null) {
-         if (other.requestFilters != null)
-            return false;
-      } else if (!requestFilters.equals(other.requestFilters))
-         return false;
-      if (!Arrays.equals(skips, other.skips))
+      if (!Objects.equal(payload, other.payload))
          return false;
       return true;
    }
diff --git a/core/src/main/java/org/jclouds/http/TransformingHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/TransformingHttpCommandExecutorService.java
index 36f15a8..317b899 100644
--- a/core/src/main/java/org/jclouds/http/TransformingHttpCommandExecutorService.java
+++ b/core/src/main/java/org/jclouds/http/TransformingHttpCommandExecutorService.java
@@ -22,6 +22,7 @@
 
 import com.google.common.base.Function;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.inject.ImplementedBy;
 
 /**
  * Executor which will invoke and transform the response of an {@code EndpointCommand} into generic
@@ -29,6 +30,7 @@
  * 
  * @author Adrian Cole
  */
+@ImplementedBy(TransformingHttpCommandExecutorServiceImpl.class)
 public interface TransformingHttpCommandExecutorService {
    /**
     * 
diff --git a/core/src/main/java/org/jclouds/http/TransformingHttpCommandImpl.java b/core/src/main/java/org/jclouds/http/TransformingHttpCommandImpl.java
index 0a26430..90f5de6 100644
--- a/core/src/main/java/org/jclouds/http/TransformingHttpCommandImpl.java
+++ b/core/src/main/java/org/jclouds/http/TransformingHttpCommandImpl.java
@@ -29,6 +29,7 @@
 import org.jclouds.rest.internal.GeneratedHttpRequest;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.util.concurrent.ListenableFuture;
 
 /**
@@ -145,6 +146,20 @@
    }
 
    @Override
+   public int hashCode() {
+      return Objects.hashCode(request);
+   }
+
+   @Override
+   public boolean equals(Object that) {
+      if (that == null)
+         return false;
+      if (!(that instanceof HttpCommand))
+         return false;
+      return Objects.equal(this.request, HttpCommand.class.cast(that).getCurrentRequest());
+   }
+
+   @Override
    public String toString() {
       if (request instanceof GeneratedHttpRequest<?>)
          return String.format("[method=%s.%s, request=%s]", GeneratedHttpRequest.class.cast(request).getDeclaring()
diff --git a/core/src/main/java/org/jclouds/http/functions/ParseXMLWithJAXB.java b/core/src/main/java/org/jclouds/http/functions/ParseXMLWithJAXB.java
index 2b7e5e8..2ed2f23 100644
--- a/core/src/main/java/org/jclouds/http/functions/ParseXMLWithJAXB.java
+++ b/core/src/main/java/org/jclouds/http/functions/ParseXMLWithJAXB.java
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.http.functions;
 
 import static org.jclouds.http.HttpUtils.releasePayload;
diff --git a/core/src/main/java/org/jclouds/javax/annotation/Nullable.java b/core/src/main/java/org/jclouds/javax/annotation/Nullable.java
index d75f256..37d9acf 100644
--- a/core/src/main/java/org/jclouds/javax/annotation/Nullable.java
+++ b/core/src/main/java/org/jclouds/javax/annotation/Nullable.java
@@ -1,3 +1,21 @@
+/**
+ * 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.javax.annotation;
 
 @java.lang.annotation.Documented
diff --git a/core/src/main/java/org/jclouds/javax/annotation/concurrent/NotThreadSafe.java b/core/src/main/java/org/jclouds/javax/annotation/concurrent/NotThreadSafe.java
index 47a4b15..b51ac76 100644
--- a/core/src/main/java/org/jclouds/javax/annotation/concurrent/NotThreadSafe.java
+++ b/core/src/main/java/org/jclouds/javax/annotation/concurrent/NotThreadSafe.java
@@ -1,3 +1,21 @@
+/**
+ * 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.javax.annotation.concurrent;
 
 @java.lang.annotation.Documented
diff --git a/core/src/main/java/org/jclouds/json/config/GsonModule.java b/core/src/main/java/org/jclouds/json/config/GsonModule.java
index aaece5b..5087037 100644
--- a/core/src/main/java/org/jclouds/json/config/GsonModule.java
+++ b/core/src/main/java/org/jclouds/json/config/GsonModule.java
@@ -27,6 +27,7 @@
 import java.util.Properties;
 
 import javax.inject.Inject;
+import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.jclouds.crypto.CryptoStreams;
@@ -35,20 +36,14 @@
 import org.jclouds.json.Json;
 import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
 import org.jclouds.json.internal.GsonWrapper;
-import org.jclouds.json.internal.JsonLiteral;
+import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
 
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.Maps;
+import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.primitives.Bytes;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
 import com.google.gson.TypeAdapter;
 import com.google.gson.internal.JsonReaderInternalAccess;
 import com.google.gson.reflect.TypeToken;
@@ -57,7 +52,6 @@
 import com.google.inject.AbstractModule;
 import com.google.inject.ImplementedBy;
 import com.google.inject.Provides;
-import com.google.inject.TypeLiteral;
 
 /**
  * Contains logic for parsing objects from Strings.
@@ -69,9 +63,9 @@
    @SuppressWarnings("rawtypes")
    @Provides
    @Singleton
-   Gson provideGson(JsonBallAdapter jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
-         ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings)
-         throws ClassNotFoundException, Exception {
+   Gson provideGson(TypeAdapter<JsonBall> jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
+            ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings)
+            throws ClassNotFoundException, Exception {
       GsonBuilder builder = new GsonBuilder();
 
       // simple (type adapters)
@@ -80,43 +74,38 @@
       builder.registerTypeAdapter(new TypeToken<List<Byte>>() {
       }.getType(), byteListAdapter.nullSafe());
       builder.registerTypeAdapter(byte[].class, byteArrayAdapter.nullSafe());
+      builder.registerTypeAdapter(JsonBall.class, jsonAdapter.nullSafe());
 
       // complicated (serializers/deserializers as they need context to operate)
       builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue());
-      builder.registerTypeAdapter(JsonBall.class, jsonAdapter);
 
       for (Map.Entry<Type, Object> binding : bindings.getBindings().entrySet()) {
          builder.registerTypeAdapter(binding.getKey(), binding.getValue());
       }
-      
+
       return builder.create();
    }
 
-   // http://code.google.com/p/google-gson/issues/detail?id=326
-   @ImplementedBy(JsonBallAdapterImpl.class)
-   public static interface JsonBallAdapter extends JsonSerializer<JsonBall>, JsonDeserializer<JsonBall> {
-
-   }
-
-   @Singleton
-   public static class JsonBallAdapterImpl implements JsonBallAdapter {
-
-      public JsonElement serialize(JsonBall src, Type typeOfSrc, JsonSerializationContext context) {
-         return new JsonLiteral(src);
-      }
-
-      public JsonBall deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
-            throws JsonParseException {
-         return new JsonBall(json.toString());
-      }
-
-   }
-
    @ImplementedBy(CDateAdapter.class)
    public static abstract class DateAdapter extends TypeAdapter<Date> {
 
    }
 
+   @Provides
+   @Singleton
+   protected TypeAdapter<JsonBall> provideJsonBallAdapter(NullHackJsonBallAdapter in) {
+      return in;
+   }
+
+   public static class NullHackJsonBallAdapter extends NullHackJsonLiteralAdapter<JsonBall> {
+
+      @Override
+      protected JsonBall createJsonLiteralFromRawJson(String json) {
+         return new JsonBall(json);
+      }
+
+   }
+
    @ImplementedBy(HexByteListAdapter.class)
    public static abstract class ByteListAdapter extends TypeAdapter<List<Byte>> {
 
@@ -185,15 +174,15 @@
 
    @Singleton
    public static class PropertiesAdapter extends TypeAdapter<Properties> {
-      private final Json json;
-      private final Type mapType = new TypeLiteral<Map<String, String>>() {
-      }.getRawType();
+      private final Provider<Gson> gson;
+      private final TypeToken<Map<String, String>> mapType = new TypeToken<Map<String, String>>() {
+      };
 
       @Inject
-      public PropertiesAdapter(Json json) {
-         this.json = json;
+      public PropertiesAdapter(Provider<Gson> gson) {
+         this.gson = gson;
       }
-      
+
       @Override
       public void write(JsonWriter out, Properties value) throws IOException {
          Builder<String, String> srcMap = ImmutableMap.<String, String> builder();
@@ -201,7 +190,7 @@
             String propName = (String) propNames.nextElement();
             srcMap.put(propName, value.getProperty(propName));
          }
-         out.value(new JsonLiteral(json.toJson(srcMap.build(), mapType)));
+         gson.get().getAdapter(mapType).write(out, srcMap.build());
       }
 
       @Override
@@ -209,8 +198,8 @@
          Properties props = new Properties();
          in.beginObject();
          while (in.hasNext()) {
-           JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
-           props.setProperty(in.nextString(), in.nextString());
+            JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
+            props.setProperty(in.nextString(), in.nextString());
          }
          in.endObject();
          return props;
diff --git a/core/src/main/java/org/jclouds/json/internal/JsonLiteral.java b/core/src/main/java/org/jclouds/json/internal/JsonLiteral.java
deleted file mode 100644
index 6fee782..0000000
--- a/core/src/main/java/org/jclouds/json/internal/JsonLiteral.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * 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.json.internal;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.gson.JsonElement;
-
-/**
- * The gson project use package to control access to their objects. However, this prevents us from
- * doing valid work, like controlling the json emitted on a per-object basis. This is here to afford
- * us to do this.
- * 
- * @author Adrian Cole
- * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
- */
-public final class JsonLiteral extends JsonElement {
-   private final CharSequence literal;
-
-   public JsonLiteral(CharSequence literal) {
-      this.literal = checkNotNull(literal, "literal");
-   }
-
-   @Override
-   public String toString() {
-      return literal.toString();
-   }
-
-}
diff --git a/core/src/main/java/org/jclouds/json/internal/NullHackJsonLiteralAdapter.java b/core/src/main/java/org/jclouds/json/internal/NullHackJsonLiteralAdapter.java
new file mode 100644
index 0000000..6efba01
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/NullHackJsonLiteralAdapter.java
@@ -0,0 +1,135 @@
+/**
+ * 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.json.internal;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Field;
+
+import javax.inject.Singleton;
+
+import com.google.common.base.Throwables;
+import com.google.gson.TypeAdapter;
+import com.google.gson.internal.bind.TypeAdapters;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * writes or reads the literal json directly
+ * 
+ * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326" />
+ * 
+ */
+@Singleton
+public abstract class NullHackJsonLiteralAdapter<L> extends TypeAdapter<L> {
+
+   @Override
+   public L read(JsonReader reader) throws IOException {
+      return createJsonLiteralFromRawJson(TypeAdapters.JSON_ELEMENT.read(reader).toString());
+   }
+
+
+   /**
+    * User supplied type that holds json literally. Ex. number as {@code 8}, boolean as {@code true}
+    * , string as {@code "value"}, object as {@code , list {@code []}.
+    */
+   protected abstract L createJsonLiteralFromRawJson(String json);
+
+   // the writer inside JsonWriter is not accessible currently
+   private static final Field outField;
+   static {
+      try {
+         outField = JsonWriter.class.getDeclaredField("out");
+      } catch (SecurityException e) {
+         throw Throwables.propagate(e);
+      } catch (NoSuchFieldException e) {
+         throw Throwables.propagate(e);
+      }
+      outField.setAccessible(true);
+   }
+
+   @Override
+   public void write(JsonWriter jsonWriter, L value) throws IOException {
+
+      Writer writer = getWriter(jsonWriter);
+      boolean serializeNulls = jsonWriter.getSerializeNulls();
+      try {
+         // we are using an implementation hack which depends on replacing null with the raw json
+         // supplied as a parameter. In this case, we must ensure we indeed serialize nulls.
+         NullReplacingWriter nullReplacingWriter = new NullReplacingWriter(writer, value.toString());
+         setWriter(jsonWriter, nullReplacingWriter);
+         jsonWriter.setSerializeNulls(true);
+         jsonWriter.nullValue();
+      } finally {
+         setWriter(jsonWriter, writer);
+         jsonWriter.setSerializeNulls(serializeNulls);
+      }
+
+   }
+
+   private Writer getWriter(JsonWriter arg0) {
+      try {
+         return Writer.class.cast(outField.get(arg0));
+      } catch (IllegalAccessException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   private void setWriter(JsonWriter arg0, Writer arg1) {
+      try {
+         outField.set(arg0, arg1);
+      } catch (IllegalAccessException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   private final class NullReplacingWriter extends Writer {
+      private final Writer delegate;
+      private final String nullReplacement;
+
+      public NullReplacingWriter(Writer delegate, String nullReplacement) {
+         this.delegate = delegate;
+         this.nullReplacement = nullReplacement;
+      }
+
+      @Override
+      public void write(char[] buffer, int offset, int count) throws IOException {
+         delegate.write(buffer, offset, count);
+      }
+
+      @Override
+      public void write(String s) throws IOException {
+         if (nullReplacement != null && s.equals("null")) {
+            s = nullReplacement;
+         }
+         super.write(s);
+      }
+
+      @Override
+      public void flush() throws IOException {
+         delegate.flush();
+      }
+
+      @Override
+      public void close() throws IOException {
+         delegate.close();
+      }
+
+   }
+}
diff --git a/core/src/main/java/org/jclouds/lifecycle/config/LifeCycleModule.java b/core/src/main/java/org/jclouds/lifecycle/config/LifeCycleModule.java
index 39b3220..6f5e85c 100644
--- a/core/src/main/java/org/jclouds/lifecycle/config/LifeCycleModule.java
+++ b/core/src/main/java/org/jclouds/lifecycle/config/LifeCycleModule.java
@@ -35,10 +35,13 @@
 import javax.inject.Named;
 
 import org.jclouds.Constants;
+import org.jclouds.concurrent.MoreExecutors;
 import org.jclouds.lifecycle.Closer;
 
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.ExecutionList;
 import com.google.inject.AbstractModule;
-import com.google.inject.ProvisionException;
+import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.InjectionListener;
 import com.google.inject.spi.TypeEncounter;
@@ -49,6 +52,15 @@
  * {@link PostConstruct} after injection, and Associate {@link PreDestroy} with a global
  * {@link Closer} object.
  * 
+ * <h3>Important</h3> Make sure you create your injector with {@link Stage#PRODUCTION} and execute
+ * the bound {@link ExecutionList} prior to using any other objects.
+ * 
+ * <p/>
+ * Ex.
+ * <pre>
+ * 
+ * </pre>
+ * 
  * @author Adrian Cole
  */
 public class LifeCycleModule extends AbstractModule {
@@ -75,10 +87,13 @@
       Closer closer = new Closer();
       closer.addToClose(executorCloser);
       bind(Closer.class).toInstance(closer);
-      bindPostInjectionInvoke(closer);
+
+      ExecutionList list = new ExecutionList();
+      bindPostInjectionInvoke(closer, list);
+      bind(ExecutionList.class).toInstance(list);
    }
 
-   protected void bindPostInjectionInvoke(final Closer closer) {
+   protected void bindPostInjectionInvoke(final Closer closer, final ExecutionList list) {
       bindListener(any(), new TypeListener() {
          public <I> void hear(TypeLiteral<I> injectableType, TypeEncounter<I> encounter) {
             Set<Method> methods = new HashSet<Method>();
@@ -93,8 +108,8 @@
             }
          }
 
-         private <I> void associatePreDestroyWithCloser(final Closer closer,
-                  TypeEncounter<I> encounter, final Method method) {
+         private <I> void associatePreDestroyWithCloser(final Closer closer, TypeEncounter<I> encounter,
+                  final Method method) {
             PreDestroy preDestroy = method.getAnnotation(PreDestroy.class);
             if (preDestroy != null) {
                encounter.register(new InjectionListener<I>() {
@@ -117,20 +132,23 @@
             }
          }
 
-         private <I> void invokePostConstructMethodAfterInjection(TypeEncounter<I> encounter,
-                  final Method method) {
+         private <I> void invokePostConstructMethodAfterInjection(TypeEncounter<I> encounter, final Method method) {
             PostConstruct postConstruct = method.getAnnotation(PostConstruct.class);
             if (postConstruct != null) {
                encounter.register(new InjectionListener<I>() {
-                  public void afterInjection(I injectee) {
-                     try {
-                        method.invoke(injectee);
-                     } catch (InvocationTargetException ie) {
-                        Throwable e = ie.getTargetException();
-                        throw new ProvisionException(e.getMessage(), e);
-                     } catch (IllegalAccessException e) {
-                        throw new ProvisionException(e.getMessage(), e);
-                     }
+                  public void afterInjection(final I injectee) {
+                     list.add(new Runnable() {
+                        public void run() {
+                           try {
+                              method.invoke(injectee);
+                           } catch (InvocationTargetException ie) {
+                              Throwable e = ie.getTargetException();
+                              throw Throwables.propagate(e);
+                           } catch (IllegalAccessException e) {
+                              throw Throwables.propagate(e);
+                           }
+                        }
+                     }, MoreExecutors.sameThreadExecutor());
                   }
                });
             }
diff --git a/core/src/main/java/org/jclouds/location/config/LocationModule.java b/core/src/main/java/org/jclouds/location/config/LocationModule.java
index 63f8cd8..8465d76 100644
--- a/core/src/main/java/org/jclouds/location/config/LocationModule.java
+++ b/core/src/main/java/org/jclouds/location/config/LocationModule.java
@@ -20,7 +20,6 @@
 
 import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
 
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -32,10 +31,7 @@
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 
-import com.google.common.base.Function;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.collect.Maps;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 
@@ -56,27 +52,6 @@
 
    @Provides
    @Singleton
-   protected Supplier<Map<String, ? extends Location>> provideLocationMap(
-            @Memoized Supplier<Set<? extends Location>> locations) {
-      return Suppliers.compose(new Function<Set<? extends Location>, Map<String, ? extends Location>>() {
-
-         @Override
-         public Map<String, ? extends Location> apply(Set<? extends Location> from) {
-            return Maps.uniqueIndex(from, new Function<Location, String>() {
-
-               @Override
-               public String apply(Location from) {
-                  return from.getId();
-               }
-
-            });
-         }
-
-      }, locations);
-   }
-
-   @Provides
-   @Singleton
    @Memoized
    protected Supplier<Set<? extends Location>> supplyLocationCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
             final Supplier<Set<? extends Location>> locationSupplier) {
diff --git a/core/src/main/java/org/jclouds/location/predicates/LocationPredicates.java b/core/src/main/java/org/jclouds/location/predicates/LocationPredicates.java
index f56ca86..73e4722 100644
--- a/core/src/main/java/org/jclouds/location/predicates/LocationPredicates.java
+++ b/core/src/main/java/org/jclouds/location/predicates/LocationPredicates.java
@@ -68,6 +68,30 @@
       }
    }
 
+   public static Predicate<Location> idEquals(String id) {
+      return new IdEquals(id);
+   }
+
+   static class IdEquals implements Predicate<Location> {
+
+      private final String id;
+
+      IdEquals(String id) {
+         this.id = checkNotNull(id, "id");
+      }
+
+      @Override
+      public boolean apply(Location input) {
+
+         return input.getId().equals(id);
+      }
+
+      @Override
+      public String toString() {
+         return "idEquals(" + id + ")";
+      }
+   }
+   
    public static Predicate<Location> isZoneOrRegionWhereRegionIdEquals(String region) {
       return new IsZoneOrRegionWhereRegionIdEquals(region);
    }
diff --git a/core/src/main/java/org/jclouds/rest/RestContext.java b/core/src/main/java/org/jclouds/rest/RestContext.java
index c07ab1c..c03665d 100644
--- a/core/src/main/java/org/jclouds/rest/RestContext.java
+++ b/core/src/main/java/org/jclouds/rest/RestContext.java
@@ -22,6 +22,7 @@
 import java.util.Map;
 import java.util.concurrent.Future;
 
+import org.jclouds.Constants;
 import org.jclouds.domain.Credentials;
 import org.jclouds.domain.Location;
 import org.jclouds.rest.internal.RestContextImpl;
@@ -58,9 +59,19 @@
    S getApi();
 
    URI getEndpoint();
-
+   
+   /**
+    * @return version of the api presented by this service
+    * @see Constants#PROPERTY_API_VERSION
+    */
    String getApiVersion();
 
+   /**
+    * @return version of software this service is running
+    * @see Constants#PROPERTY_BUILD_VERSION
+    */
+   String getBuildVersion();
+
    String getIdentity();
 
    /**
diff --git a/core/src/main/java/org/jclouds/rest/RestContextBuilder.java b/core/src/main/java/org/jclouds/rest/RestContextBuilder.java
index 94894c6..e60d9bd 100644
--- a/core/src/main/java/org/jclouds/rest/RestContextBuilder.java
+++ b/core/src/main/java/org/jclouds/rest/RestContextBuilder.java
@@ -31,6 +31,7 @@
 import static com.google.inject.util.Types.newParameterizedType;
 import static org.jclouds.Constants.PROPERTY_API;
 import static org.jclouds.Constants.PROPERTY_API_VERSION;
+import static org.jclouds.Constants.PROPERTY_BUILD_VERSION;
 import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
 import static org.jclouds.Constants.PROPERTY_ENDPOINT;
 import static org.jclouds.Constants.PROPERTY_IDENTITY;
@@ -39,14 +40,17 @@
 import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
 
 import java.net.URI;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
 import java.util.Map.Entry;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import com.google.common.collect.*;
 import org.jclouds.concurrent.MoreExecutors;
 import org.jclouds.concurrent.SingleThreaded;
 import org.jclouds.concurrent.config.ConfiguresExecutorService;
@@ -54,6 +58,7 @@
 import org.jclouds.http.RequiresHttp;
 import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
 import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
+import org.jclouds.lifecycle.config.LifeCycleModule;
 import org.jclouds.location.Iso3166;
 import org.jclouds.location.Provider;
 import org.jclouds.location.config.ProvideIso3166CodesByLocationIdViaProperties;
@@ -61,6 +66,7 @@
 import org.jclouds.logging.jdk.config.JDKLoggingModule;
 import org.jclouds.rest.annotations.Api;
 import org.jclouds.rest.annotations.ApiVersion;
+import org.jclouds.rest.annotations.BuildVersion;
 import org.jclouds.rest.annotations.Credential;
 import org.jclouds.rest.annotations.Identity;
 import org.jclouds.rest.config.CredentialStoreModule;
@@ -70,12 +76,19 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.ExecutionList;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Module;
 import com.google.inject.Provides;
+import com.google.inject.Stage;
 import com.google.inject.TypeLiteral;
 
 /**
@@ -149,6 +162,8 @@
             bind(String.class).annotatedWith(Api.class).toInstance(toBind.getProperty(PROPERTY_API));
          if (toBind.containsKey(PROPERTY_API_VERSION))
             bind(String.class).annotatedWith(ApiVersion.class).toInstance(toBind.getProperty(PROPERTY_API_VERSION));
+         if (toBind.containsKey(PROPERTY_BUILD_VERSION))
+            bind(String.class).annotatedWith(BuildVersion.class).toInstance(toBind.getProperty(PROPERTY_BUILD_VERSION));
          if (toBind.containsKey(PROPERTY_IDENTITY))
             bind(String.class).annotatedWith(Identity.class).toInstance(
                      checkNotNull(toBind.getProperty(PROPERTY_IDENTITY), PROPERTY_IDENTITY));
@@ -182,8 +197,11 @@
       ifHttpConfigureRestOtherwiseGuiceClientFactory(modules);
       addExecutorServiceIfNotPresent(modules);
       addCredentialStoreIfNotPresent(modules);
+      modules.add(new LifeCycleModule());
       modules.add(new BindPropertiesAndPrincipalContext(properties));
-      return Guice.createInjector(modules);
+      Injector returnVal = Guice.createInjector(Stage.PRODUCTION, modules);
+      returnVal.getInstance(ExecutionList.class).execute();
+      return returnVal;
    }
 
    @VisibleForTesting
diff --git a/core/src/main/java/org/jclouds/rest/RestContextFactory.java b/core/src/main/java/org/jclouds/rest/RestContextFactory.java
index bc88244..4aacd74 100644
--- a/core/src/main/java/org/jclouds/rest/RestContextFactory.java
+++ b/core/src/main/java/org/jclouds/rest/RestContextFactory.java
@@ -30,10 +30,10 @@
 import java.io.IOException;
 import java.util.Properties;
 
-import org.jclouds.javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.jclouds.PropertiesBuilder;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.reference.LocationConstants;
 import org.jclouds.util.ClassLoadingUtils;
 import org.jclouds.util.Modules2;
@@ -72,24 +72,24 @@
 public class RestContextFactory {
 
    public static <S, A> RestContextSpec<S, A> contextSpec(String provider, String endpoint, String apiVersion,
-         String iso3166Codes, String identity, String credential, Class<S> sync, Class<A> async,
+         String buildVersion, String iso3166Codes, String identity, String credential, Class<S> sync, Class<A> async,
          Class<PropertiesBuilder> propertiesBuilderClass, Class<RestContextBuilder<S, A>> contextBuilderClass,
          Iterable<Module> modules) {
-      return new RestContextSpec<S, A>(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async,
-            propertiesBuilderClass, contextBuilderClass, modules);
+      return new RestContextSpec<S, A>(provider, endpoint, apiVersion, buildVersion, iso3166Codes, identity,
+               credential, sync, async, propertiesBuilderClass, contextBuilderClass, modules);
    }
 
-   public static <S, A> RestContextSpec<S, A> contextSpec(String provider, String endpoint, String apiVersion,
+   public static <S, A> RestContextSpec<S, A> contextSpec(String provider, String endpoint, String apiVersion,String buildVersion, 
          String iso3166Codes, String identity, String credential, Class<S> sync, Class<A> async) {
-      return new RestContextSpec<S, A>(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async);
+      return new RestContextSpec<S, A>(provider, endpoint, apiVersion, buildVersion, iso3166Codes, identity, credential, sync, async);
    }
 
-   @SuppressWarnings({ "unchecked", "rawtypes" })
+   @SuppressWarnings( { "unchecked", "rawtypes" })
    public static <S, A> RestContextSpec<S, A> contextSpec(String provider, String endpoint, String apiVersion,
-         String iso3166Codes, String identity, String credential, Class<S> sync, Class<A> async,
-         Iterable<Module> modules) {
-      return new RestContextSpec<S, A>(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async,
-            PropertiesBuilder.class, (Class) RestContextBuilder.class, modules);
+            String buildVersion, String iso3166Codes, String identity, String credential, Class<S> sync,
+            Class<A> async, Iterable<Module> modules) {
+      return new RestContextSpec<S, A>(provider, endpoint, apiVersion, buildVersion, iso3166Codes, identity,
+               credential, sync, async, PropertiesBuilder.class, (Class) RestContextBuilder.class, modules);
    }
 
    private final static Properties NO_PROPERTIES = new Properties();
@@ -241,7 +241,8 @@
       Properties props = new Properties();
 
       props.setProperty(contextSpec.provider + ".endpoint", contextSpec.endpoint);
-      props.setProperty(contextSpec.provider + ".apiversion", contextSpec.apiVersion);
+      props.setProperty(contextSpec.provider + ".api-version", contextSpec.apiVersion);
+      props.setProperty(contextSpec.provider + ".build-version", contextSpec.buildVersion);
       props.setProperty(contextSpec.provider + "." + LocationConstants.ISO3166_CODES, contextSpec.iso3166Codes);
       props.setProperty(contextSpec.provider + ".identity", contextSpec.identity);
       if (contextSpec.credential != null)
@@ -289,7 +290,8 @@
 
       String endpoint = props.getProperty(providerName + "." + LocationConstants.ENDPOINT, null);
       String iso3166Codes = props.getProperty(providerName + "." + LocationConstants.ISO3166_CODES, null);
-      String apiVersion = props.getProperty(providerName + ".apiversion", null);
+      String apiVersion = props.getProperty(providerName + ".api-version", null);
+      String buildVersion = props.getProperty(providerName + ".build-version", null);
       identity = props.getProperty(providerName + ".identity", props.getProperty("jclouds.identity", identity));
       credential = loadCredentialOrDefault(props, providerName + ".credential",
             loadCredentialOrDefault(props, "jclouds.credential", credential));
@@ -316,7 +318,7 @@
          assert false : "exception should have propogated " + e;
          return null;
       }
-      RestContextSpec<S, A> contextSpec = new RestContextSpec<S, A>(providerName, endpoint, apiVersion, iso3166Codes,
+      RestContextSpec<S, A> contextSpec = new RestContextSpec<S, A>(providerName, endpoint, apiVersion, buildVersion, iso3166Codes,
             identity, credential, sync, async, propertiesBuilderClass, contextBuilderClass, modules);
       return contextSpec;
    }
@@ -365,6 +367,8 @@
          builder.provider(contextSpec.provider);
          if (contextSpec.apiVersion != null)
             builder.apiVersion(contextSpec.apiVersion);
+         if (contextSpec.buildVersion != null)
+            builder.buildVersion(contextSpec.buildVersion);
          if (contextSpec.iso3166Codes != null)
             builder.iso3166Codes(Splitter.on('.').split(contextSpec.iso3166Codes));
          if (contextSpec.identity != null)
diff --git a/core/src/main/java/org/jclouds/rest/RestContextSpec.java b/core/src/main/java/org/jclouds/rest/RestContextSpec.java
index a1a4cef..9cbea95 100644
--- a/core/src/main/java/org/jclouds/rest/RestContextSpec.java
+++ b/core/src/main/java/org/jclouds/rest/RestContextSpec.java
@@ -35,6 +35,7 @@
    protected final String provider;
    protected final String endpoint;
    protected final String apiVersion;
+   protected final String buildVersion;
    protected final String iso3166Codes;
    protected final String identity;
    protected final String credential;
@@ -44,12 +45,13 @@
    protected final Class<RestContextBuilder<S, A>> contextBuilderClass;
    protected final Iterable<Module> modules;
 
-   public RestContextSpec(String provider, String endpoint, String apiVersion, String iso3166Codes, String identity,
+   public RestContextSpec(String provider, String endpoint, String apiVersion, String buildVersion, String iso3166Codes, String identity,
             String credential, Class<S> sync, Class<A> async, Class<PropertiesBuilder> propertiesBuilderClass,
             Class<RestContextBuilder<S, A>> contextBuilderClass, Iterable<Module> modules) {
       this.provider = checkNotNull(provider, "provider");
       this.endpoint = endpoint;
       this.apiVersion = apiVersion;
+      this.buildVersion = buildVersion;
       this.identity = identity;
       this.credential = credential;
       this.iso3166Codes = iso3166Codes;
@@ -65,26 +67,19 @@
    }
 
    @SuppressWarnings( { "unchecked", "rawtypes" })
-   public RestContextSpec(String provider, String endpoint, String apiVersion, String iso3166Codes, String identity,
+   public RestContextSpec(String provider, String endpoint, String apiVersion, String buildVersion, String iso3166Codes, String identity,
             String credential, Class<S> sync, Class<A> async) {
-      this(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async, PropertiesBuilder.class,
+      this(provider, endpoint, apiVersion, buildVersion, iso3166Codes, identity, credential, sync, async, PropertiesBuilder.class,
                (Class) RestContextBuilder.class, EMPTY_LIST);
    }
 
-   /**
-    * this uses the inefficient {@link Objects} implementation as the object count will be
-    * relatively small and therefore efficiency is not a concern.
-    */
+
    @Override
    public int hashCode() {
-      return Objects.hashCode(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async,
+      return Objects.hashCode(provider, endpoint, apiVersion, buildVersion, iso3166Codes, identity, credential, sync, async,
                propertiesBuilderClass, contextBuilderClass, modules);
    }
 
-   /**
-    * this uses the inefficient {@link Objects} implementation as the object count will be
-    * relatively small and therefore efficiency is not a concern.
-    */
    @Override
    public boolean equals(Object that) {
       if (that == null)
@@ -92,16 +87,13 @@
       return Objects.equal(this.toString(), that.toString());
    }
 
-   /**
-    * this uses the inefficient {@link Objects} implementation as the object count will be
-    * relatively small and therefore efficiency is not a concern.
-    */
    @Override
    public String toString() {
       return Objects.toStringHelper(this).add("provider", provider).add("endpoint", endpoint).add("apiVersion",
-               apiVersion).add("iso3166Codes", iso3166Codes).add("identity", identity).add("sync", sync).add("async",
-               async).add("propertiesBuilderClass", propertiesBuilderClass).add("contextBuilderClass",
-               contextBuilderClass).add("modules", modules).toString();
+               apiVersion).add("buildVersion", buildVersion).add("iso3166Codes", iso3166Codes)
+               .add("identity", identity).add("sync", sync).add("async", async).add("propertiesBuilderClass",
+                        propertiesBuilderClass).add("contextBuilderClass", contextBuilderClass).add("modules", modules)
+               .toString();
    }
 
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/rest/annotations/ApiVersion.java b/core/src/main/java/org/jclouds/rest/annotations/ApiVersion.java
index 6f2aab0..210a598 100644
--- a/core/src/main/java/org/jclouds/rest/annotations/ApiVersion.java
+++ b/core/src/main/java/org/jclouds/rest/annotations/ApiVersion.java
@@ -28,10 +28,13 @@
 
 import javax.inject.Qualifier;
 
+import org.jclouds.Constants;
+
 /**
  * Designates that this Resource qualifies an object to an api version.
  * 
  * @author Adrian Cole
+ * @see Constants#PROPERTY_API_VERSION
  */
 @Target( { ANNOTATION_TYPE, FIELD, PARAMETER })
 @Retention(RUNTIME)
diff --git a/core/src/main/java/org/jclouds/rest/annotations/BuildVersion.java b/core/src/main/java/org/jclouds/rest/annotations/BuildVersion.java
new file mode 100644
index 0000000..3586a3c
--- /dev/null
+++ b/core/src/main/java/org/jclouds/rest/annotations/BuildVersion.java
@@ -0,0 +1,44 @@
+/**
+ * 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.rest.annotations;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+import org.jclouds.Constants;
+
+/**
+ * Designates that this Resource qualifies an object to an build version.
+ * 
+ * @author Adrian Cole
+ * @see Constants#PROPERTY_BUILD_VERSION
+ */
+@Target( { ANNOTATION_TYPE, FIELD, PARAMETER })
+@Retention(RUNTIME)
+@Qualifier
+public @interface BuildVersion {
+
+}
diff --git a/core/src/main/java/org/jclouds/rest/binders/BindException.java b/core/src/main/java/org/jclouds/rest/binders/BindException.java
index fe2fcbf..6e52cd2 100644
--- a/core/src/main/java/org/jclouds/rest/binders/BindException.java
+++ b/core/src/main/java/org/jclouds/rest/binders/BindException.java
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.rest.binders;
 
 import org.jclouds.http.HttpRequest;
diff --git a/core/src/main/java/org/jclouds/rest/binders/BindToXMLPayload.java b/core/src/main/java/org/jclouds/rest/binders/BindToXMLPayload.java
index 6adfecb..87a31fe 100644
--- a/core/src/main/java/org/jclouds/rest/binders/BindToXMLPayload.java
+++ b/core/src/main/java/org/jclouds/rest/binders/BindToXMLPayload.java
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.rest.binders;
 
 import static com.google.common.base.Preconditions.checkNotNull;
diff --git a/core/src/main/java/org/jclouds/rest/config/RestClientModule.java b/core/src/main/java/org/jclouds/rest/config/RestClientModule.java
index 3041976..c6c56e6 100644
--- a/core/src/main/java/org/jclouds/rest/config/RestClientModule.java
+++ b/core/src/main/java/org/jclouds/rest/config/RestClientModule.java
@@ -29,8 +29,8 @@
 import org.jclouds.rest.RestContext;
 import org.jclouds.rest.internal.RestContextImpl;
 
-import com.google.common.cache.LoadingCache;
 import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
@@ -71,6 +71,9 @@
             (TypeLiteral) TypeLiteral.get(Types.newParameterizedType(
                   RestContextImpl.class, syncClientType, asyncClientType))).in(
             Scopes.SINGLETON);
+      bind(TypeLiteral.get(Types.newParameterizedType(RestContext.class, syncClientType, asyncClientType))).to(
+               (TypeLiteral) TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class, syncClientType,
+                        asyncClientType))).in(Scopes.SINGLETON);
       bindAsyncClient();
       bindClient();
       bindErrorHandlers();
diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java
index 4b2133d..1a26d26 100644
--- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java
+++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java
@@ -47,13 +47,12 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedSet;
+import java.util.Map.Entry;
 import java.util.concurrent.ExecutionException;
 
 import javax.annotation.Resource;
-import javax.inject.Named;
 import javax.inject.Provider;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
@@ -78,7 +77,6 @@
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
 import org.jclouds.http.functions.ParseJson;
 import org.jclouds.http.functions.ParseSax;
-import org.jclouds.http.functions.ParseSax.HandlerWithResult;
 import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
 import org.jclouds.http.functions.ParseXMLWithJAXB;
 import org.jclouds.http.functions.ReleasePayloadAndReturn;
@@ -86,6 +84,7 @@
 import org.jclouds.http.functions.ReturnStringIf2xx;
 import org.jclouds.http.functions.ReturnTrueIf2xx;
 import org.jclouds.http.functions.UnwrapOnlyJsonValue;
+import org.jclouds.http.functions.ParseSax.HandlerWithResult;
 import org.jclouds.http.options.HttpRequestOptions;
 import org.jclouds.http.utils.ModifyRequest;
 import org.jclouds.internal.ClassMethodArgs;
@@ -102,7 +101,9 @@
 import org.jclouds.rest.Binder;
 import org.jclouds.rest.InputParamValidator;
 import org.jclouds.rest.InvocationContext;
+import org.jclouds.rest.annotations.ApiVersion;
 import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.BuildVersion;
 import org.jclouds.rest.annotations.Endpoint;
 import org.jclouds.rest.annotations.EndpointParam;
 import org.jclouds.rest.annotations.ExceptionParser;
@@ -145,12 +146,12 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.LinkedListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
+import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
@@ -245,6 +246,7 @@
    private final Provider<UriBuilder> uriBuilderProvider;
    private final LoadingCache<Class<?>, Boolean> seedAnnotationCache;
    private final String apiVersion;
+   private final String buildVersion;
    private char[] skips;
 
    @Inject
@@ -306,8 +308,8 @@
    @SuppressWarnings("unchecked")
    @Inject
    public RestAnnotationProcessor(Injector injector, LoadingCache<Class<?>, Boolean> seedAnnotationCache,
-         @Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils,
-         TypeLiteral<T> typeLiteral) throws ExecutionException {
+            @ApiVersion String apiVersion, @BuildVersion String buildVersion, ParseSax.Factory parserFactory,
+            HttpUtils utils, TypeLiteral<T> typeLiteral) throws ExecutionException {
       this.declaring = (Class<T>) typeLiteral.getRawType();
       this.injector = injector;
       this.parserFactory = parserFactory;
@@ -321,6 +323,7 @@
          skips = new char[] {};
       }
       this.apiVersion = apiVersion;
+      this.buildVersion = buildVersion;
    }
 
    public Method getDelegateOrNull(Method in) {
@@ -435,6 +438,7 @@
          Multimap<String, String> tokenValues = LinkedHashMultimap.create();
 
          tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion);
+         tokenValues.put(Constants.PROPERTY_BUILD_VERSION, buildVersion);
 
          tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder));
 
diff --git a/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java b/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java
index 2a83936..5c8f2f3 100644
--- a/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java
+++ b/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java
@@ -36,8 +36,10 @@
 import org.jclouds.rest.RestContext;
 import org.jclouds.rest.Utils;
 import org.jclouds.rest.annotations.ApiVersion;
+import org.jclouds.rest.annotations.BuildVersion;
 import org.jclouds.rest.annotations.Identity;
 
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Injector;
 import com.google.inject.Key;
@@ -59,6 +61,7 @@
    private final String identity;
    private final String provider;
    private final String apiVersion;
+   private final String buildVersion;
    private final Utils utils;
    private final Map<String, Credentials> credentialStore;
    private final Set<String> iso3166Codes;
@@ -66,7 +69,8 @@
    @Inject
    protected RestContextImpl(Closer closer, Map<String, Credentials> credentialStore, Utils utils, Injector injector,
             TypeLiteral<S> syncApi, TypeLiteral<A> asyncApi, @Provider URI endpoint, @Provider String provider,
-            @Identity String identity, @ApiVersion String apiVersion, @Iso3166 Set<String> iso3166Codes) {
+            @Identity String identity, @ApiVersion String apiVersion, @BuildVersion String buildVersion,
+            @Iso3166 Set<String> iso3166Codes) {
       this.credentialStore = credentialStore;
       this.utils = utils;
       this.asyncApi = injector.getInstance(Key.get(asyncApi));
@@ -76,6 +80,7 @@
       this.identity = identity;
       this.provider = provider;
       this.apiVersion = apiVersion;
+      this.buildVersion = buildVersion;
       this.iso3166Codes = iso3166Codes;
    }
 
@@ -127,16 +132,16 @@
    public String getApiVersion() {
       return apiVersion;
    }
+   
+   @Override
+   public String getBuildVersion() {
+      return buildVersion;
+   }
+
 
    @Override
    public int hashCode() {
-      final int prime = 31;
-      int result = 1;
-      result = prime * result + ((apiVersion == null) ? 0 : apiVersion.hashCode());
-      result = prime * result + ((endpoint == null) ? 0 : endpoint.hashCode());
-      result = prime * result + ((identity == null) ? 0 : identity.hashCode());
-      result = prime * result + ((provider == null) ? 0 : provider.hashCode());
-      return result;
+      return Objects.hashCode(provider, endpoint, apiVersion, buildVersion, identity);
    }
 
    @Override
@@ -147,34 +152,17 @@
          return false;
       if (getClass() != obj.getClass())
          return false;
-      RestContextImpl<?, ?> other = (RestContextImpl<?, ?>) obj;
-      if (apiVersion == null) {
-         if (other.apiVersion != null)
-            return false;
-      } else if (!apiVersion.equals(other.apiVersion))
-         return false;
-      if (endpoint == null) {
-         if (other.endpoint != null)
-            return false;
-      } else if (!endpoint.equals(other.endpoint))
-         return false;
-      if (identity == null) {
-         if (other.identity != null)
-            return false;
-      } else if (!identity.equals(other.identity))
-         return false;
-      if (provider == null) {
-         if (other.provider != null)
-            return false;
-      } else if (!provider.equals(other.provider))
-         return false;
-      return true;
+      RestContextImpl<?, ?> that = (RestContextImpl<?, ?>) obj;
+      return Objects.equal(this.provider, that.provider) && Objects.equal(this.endpoint, that.endpoint)
+               && Objects.equal(this.apiVersion, that.apiVersion)
+               && Objects.equal(this.buildVersion, that.buildVersion) && Objects.equal(this.identity, that.identity);
    }
 
    @Override
    public String toString() {
-      return " [id=" + provider + ", endpoint=" + endpoint + ", apiVersion=" + apiVersion + ", identity=" + identity
-               + ", iso3166Codes=" + iso3166Codes + "]";
+      return Objects.toStringHelper("").add("provider", provider).add("endpoint", endpoint).add("apiVersion",
+               apiVersion).add("buildVersion", buildVersion).add("identity", identity)
+               .add("iso3166Codes", iso3166Codes).toString();
    }
 
    @Override
@@ -204,7 +192,8 @@
 
    @Override
    public Map<String, Object> getMetadata() {
-      return ImmutableMap.<String, Object> of("endpoint", endpoint, "apiVersion", apiVersion, "identity", identity);
+      return ImmutableMap.<String, Object> of("endpoint", endpoint, "apiVersion", apiVersion, "buildVersion",
+               buildVersion, "identity", identity);
    }
 
    @Override
diff --git a/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
index 3c7de37..6fa91df 100644
--- a/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
+++ b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
@@ -18,7 +18,7 @@
  */
 package org.jclouds.rest.suppliers;
 
-import static com.google.common.base.Suppliers.memoizeWithExpiration;
+import static org.jclouds.util.Suppliers2.memoizeWithExpirationOnAbsoluteInterval;
 
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -49,7 +49,7 @@
 
    public MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
          AtomicReference<AuthorizationException> authException, long seconds, Supplier<T> delegate) {
-      this.delegate = memoizeWithExpiration(new RetryOnTimeOutExceptionSupplier<T>(
+      this.delegate = memoizeWithExpirationOnAbsoluteInterval(new RetryOnTimeOutExceptionSupplier<T>(
             new SetAndThrowAuthorizationExceptionSupplier<T>(delegate, authException)), seconds, TimeUnit.SECONDS);
       this.seconds = seconds;
    }
diff --git a/core/src/main/java/org/jclouds/util/Patterns.java b/core/src/main/java/org/jclouds/util/Patterns.java
index b0b5052..2e9a38d 100644
--- a/core/src/main/java/org/jclouds/util/Patterns.java
+++ b/core/src/main/java/org/jclouds/util/Patterns.java
@@ -39,6 +39,7 @@
    public static final Pattern PATTERN_THAT_BREAKS_URI = Pattern.compile("[a-z0-9]+://.*/.*@.*");
    public static final Pattern JSON_STRING_PATTERN = Pattern.compile("^[^\"\\{\\[].*[^\\{\\[\"]$");
    public static final Pattern JSON_NUMBER_PATTERN = Pattern.compile("^[0-9]*\\.?[0-9]*$");
+   public static final Pattern JSON_BOOLEAN_PATTERN = Pattern.compile("^(true|false)$");
    public static final Pattern PLUS_PATTERN = Pattern.compile("\\+");
    public static final Pattern STAR_PATTERN = Pattern.compile("\\*");
    public static final Pattern _7E_PATTERN = Pattern.compile("%7E");
diff --git a/core/src/main/java/org/jclouds/util/Suppliers2.java b/core/src/main/java/org/jclouds/util/Suppliers2.java
index 0df90ab..bbfa6a1 100644
--- a/core/src/main/java/org/jclouds/util/Suppliers2.java
+++ b/core/src/main/java/org/jclouds/util/Suppliers2.java
@@ -22,7 +22,12 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.concurrent.TimeUnit;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
 import com.google.common.io.OutputSupplier;
 
 /**
@@ -44,5 +49,63 @@
       };
    }
 
-  
+   /**
+    * See Supplier.memoizeWithExpiration.
+    * 
+    * Difference between this impl and v11.0 is that we fix http://code.google.com/p/guava-libraries/issues/detail?id=857.
+    */
+   public static <T> Supplier<T> memoizeWithExpirationOnAbsoluteInterval(Supplier<T> delegate, long duration, TimeUnit unit) {
+      return new ExpiringMemoizingSupplier<T>(delegate, duration, unit);
+   }
+
+   @VisibleForTesting
+   static class ExpiringMemoizingSupplier<T> implements Supplier<T>, Serializable {
+      final Supplier<T> delegate;
+      final long durationNanos;
+      transient volatile T value;
+      // The special value 0 means "not yet initialized".
+      transient volatile long expirationNanos;
+
+      ExpiringMemoizingSupplier(Supplier<T> delegate, long duration, TimeUnit unit) {
+         this.delegate = Preconditions.checkNotNull(delegate);
+         this.durationNanos = unit.toNanos(duration);
+         Preconditions.checkArgument(duration > 0);
+      }
+
+      @Override
+      public T get() {
+         // Another variant of Double Checked Locking.
+         //
+         // We use two volatile reads. We could reduce this to one by
+         // putting our fields into a holder class, but (at least on x86)
+         // the extra memory consumption and indirection are more
+         // expensive than the extra volatile reads.
+         long nanos = expirationNanos;
+         long now = System.nanoTime();
+         if (nanos == 0 || now - nanos >= 0) {
+            synchronized (this) {
+               if (nanos == expirationNanos) { // recheck for lost race
+                  
+                  // Set value to null prior to retrieving new val, so old and new are not held in memory simultaneously
+                  value = null;
+                  
+                  T t = delegate.get();
+                  value = t;
+
+                  // Update now so that, if call was expensive, we keep value for the full duration
+                  now = System.nanoTime();
+                  
+                  nanos = now + durationNanos;
+                  // In the very unlikely event that nanos is 0, set it to 1;
+                  // no one will notice 1 ns of tardiness.
+                  expirationNanos = (nanos == 0) ? 1 : nanos;
+                  return t;
+               }
+            }
+         }
+         return value;
+      }
+
+      private static final long serialVersionUID = 0;
+   }
 }
diff --git a/core/src/main/java/org/jclouds/util/Throwables2.java b/core/src/main/java/org/jclouds/util/Throwables2.java
index b7c1de8..559dfe7 100644
--- a/core/src/main/java/org/jclouds/util/Throwables2.java
+++ b/core/src/main/java/org/jclouds/util/Throwables2.java
@@ -80,12 +80,15 @@
             return (Exception) throwable;
          }
       }
-      for (Class<Exception> propagatableExceptionType : new Class[] { IllegalStateException.class,
+      for (Class<Exception> propagatableExceptionType : new Class[] { IllegalStateException.class, AssertionError.class,
                UnsupportedOperationException.class, IllegalArgumentException.class, AuthorizationException.class,
                ResourceNotFoundException.class, InsufficientResourcesException.class, HttpResponseException.class }) {
          Throwable throwable = getFirstThrowableOfType(exception, propagatableExceptionType);
          if (throwable != null) {
-            throw (Exception) throwable;
+            if (throwable instanceof AssertionError)
+               throw (AssertionError) throwable;
+            else
+               throw (Exception) throwable;
          }
       }
       Throwables.propagateIfPossible(exception.getCause(), Exception.class);
diff --git a/core/src/main/java/org/jclouds/xml/XMLParser.java b/core/src/main/java/org/jclouds/xml/XMLParser.java
index e79c521..cfb4e48 100644
--- a/core/src/main/java/org/jclouds/xml/XMLParser.java
+++ b/core/src/main/java/org/jclouds/xml/XMLParser.java
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.xml;
 
 import java.io.IOException;
diff --git a/core/src/main/java/org/jclouds/xml/internal/JAXBParser.java b/core/src/main/java/org/jclouds/xml/internal/JAXBParser.java
index 928c725..65c5633 100644
--- a/core/src/main/java/org/jclouds/xml/internal/JAXBParser.java
+++ b/core/src/main/java/org/jclouds/xml/internal/JAXBParser.java
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.xml.internal;
 
 import java.io.IOException;
diff --git a/core/src/main/resources/rest.properties b/core/src/main/resources/rest.properties
index 854c6c5..9120345 100644
--- a/core/src/main/resources/rest.properties
+++ b/core/src/main/resources/rest.properties
@@ -4,7 +4,7 @@
 azurequeue.contextbuilder=org.jclouds.azure.storage.AzureStorageContextBuilder
 azurequeue.sync=org.jclouds.azurequeue.AzureQueueClient
 azurequeue.async=org.jclouds.azurequeue.AzureQueueAsyncClient
-azurequeue.apiversion=2009-09-19
+azurequeue.api-version=2009-09-19
 azurequeue.propertiesbuilder=org.jclouds.azure.storage.AzureStoragePropertiesBuilder
 azurequeue.endpoint=https://{identity}.queue.core.windows.net
 
@@ -100,7 +100,7 @@
 
 elasticstack.propertiesbuilder=org.jclouds.elasticstack.ElasticStackPropertiesBuilder
 elasticstack.contextbuilder=org.jclouds.elasticstack.ElasticStackContextBuilder
-elasticstack.apiversion=1.0
+elasticstack.api-version=1.0
 
 elastichosts-lon-p.propertiesbuilder=org.jclouds.elastichosts.ElasticHostsPeer1LondonPropertiesBuilder
 elastichosts-lon-p.contextbuilder=org.jclouds.elasticstack.ElasticStackContextBuilder
@@ -153,7 +153,7 @@
 
 atmos.contextbuilder=org.jclouds.atmos.AtmosContextBuilder
 atmos.endpoint=https://accesspoint.atmosonline.com
-atmos.apiversion=1.3.0
+atmos.api-version=1.3.0
 
 synaptic-storage.contextbuilder=org.jclouds.atmos.AtmosContextBuilder
 synaptic-storage.propertiesbuilder=org.jclouds.synaptic.storage.SynapticStoragePropertiesBuilder
diff --git a/core/src/main/resources/rest.properties.orig b/core/src/main/resources/rest.properties.orig
deleted file mode 100644
index 11ee701..0000000
--- a/core/src/main/resources/rest.properties.orig
+++ /dev/null
@@ -1,106 +0,0 @@
-azurequeue.contextbuilder=org.jclouds.azure.storage.AzureStorageContextBuilder

-azurequeue.sync=org.jclouds.azure.storage.queue.AzureQueueClient

-azurequeue.async=org.jclouds.azure.storage.queue.AzureQueueAsyncClient

-azurequeue.propertiesbuilder=org.jclouds.azure.storage.AzureStoragePropertiesBuilder

-azurequeue.endpoint=https://{identity}.queue.core.windows.net

-

-azureblob.contextbuilder=org.jclouds.azure.storage.blob.AzureBlobContextBuilder

-azureblob.propertiesbuilder=org.jclouds.azure.storage.AzureStoragePropertiesBuilder

-azureblob.endpoint=https://{identity}.blob.core.windows.net

-

-pcs.contextbuilder=org.jclouds.mezeo.pcs2.PCSContextBuilder

-pcs.apiVersion=unknown

-

-sdn.contextbuilder=org.jclouds.nirvanix.sdn.SDNContextBuilder

-sdn.propertiesbuilder=org.jclouds.nirvanix.sdn.SDNPropertiesBuilder

-

-sqs.contextbuilder=org.jclouds.aws.sqs.SQSContextBuilder

-sqs.propertiesbuilder=org.jclouds.aws.sqs.SQSPropertiesBuilder

-

-elb.contextbuilder=org.jclouds.aws.elb.ELBContextBuilder

-elb.propertiesbuilder=org.jclouds.aws.elb.ELBPropertiesBuilder

-

-cloudwatch.contextbuilder=org.jclouds.aws.cloudwatch.CloudWatchContextBuilder

-cloudwatch.propertiesbuilder=org.jclouds.aws.cloudwatch.CloudWatchPropertiesBuilder

-

-s3.contextbuilder=org.jclouds.aws.s3.S3ContextBuilder

-s3.propertiesbuilder=org.jclouds.aws.s3.S3PropertiesBuilder

-

-ec2.contextbuilder=org.jclouds.aws.ec2.EC2ContextBuilder

-ec2.propertiesbuilder=org.jclouds.aws.ec2.EC2PropertiesBuilder

-

-rimuhosting.contextbuilder=org.jclouds.rimuhosting.miro.RimuHostingContextBuilder

-rimuhosting.propertiesbuilder=org.jclouds.rimuhosting.miro.RimuHostingPropertiesBuilder

-

-slicehost.contextbuilder=org.jclouds.slicehost.SlicehostContextBuilder

-slicehost.propertiesbuilder=org.jclouds.slicehost.SlicehostPropertiesBuilder

-

-trmk-vcloudexpress.contextbuilder=org.jclouds.vcloud.terremark.TerremarkVCloudExpressContextBuilder

-trmk-vcloudexpress.propertiesbuilder=org.jclouds.vcloud.terremark.TerremarkVCloudExpressPropertiesBuilder

-

-trmk-ecloud.contextbuilder=org.jclouds.vcloud.terremark.TerremarkECloudContextBuilder

-trmk-ecloud.propertiesbuilder=org.jclouds.vcloud.terremark.TerremarkECloudPropertiesBuilder

-

-chef.contextbuilder=org.jclouds.chef.ChefContextBuilder

-chef.propertiesbuilder=org.jclouds.chef.ChefPropertiesBuilder

-

-transientchef.contextbuilder=org.jclouds.chef.test.TransientChefContextBuilder

-transientchef.propertiesbuilder=org.jclouds.chef.ChefPropertiesBuilder

-

-opscodeplatform.contextbuilder=org.jclouds.opscodeplatform.OpscodePlatformContextBuilder

-opscodeplatform.propertiesbuilder=org.jclouds.opscodeplatform.OpscodePlatformPropertiesBuilder

-

-vcloud.contextbuilder=org.jclouds.vcloud.VCloudContextBuilder

-vcloud.propertiesbuilder=org.jclouds.vcloud.VCloudPropertiesBuilder

-

-vcloudexpress.contextbuilder=org.jclouds.vcloud.VCloudExpressContextBuilder

-vcloudexpress.propertiesbuilder=org.jclouds.vcloud.VCloudExpressPropertiesBuilder

-

-eucalyptus.contextbuilder=org.jclouds.aws.ec2.EC2ContextBuilder

-eucalyptus.propertiesbuilder=org.jclouds.aws.ec2.EucalyptusPropertiesBuilder

-

-nova.contextbuilder=org.jclouds.aws.ec2.EC2ContextBuilder

-nova.propertiesbuilder=org.jclouds.aws.ec2.NovaPropertiesBuilder

-

-cloudservers.contextbuilder=org.jclouds.rackspace.cloudservers.CloudServersContextBuilder

-cloudservers.propertiesbuilder=org.jclouds.rackspace.RackspacePropertiesBuilder

-

-bluelock-vcdirector.contextbuilder=org.jclouds.vcloud.bluelock.BlueLockVCloudDirectorContextBuilder

-bluelock-vcdirector.propertiesbuilder=org.jclouds.vcloud.bluelock.BlueLockVCloudDirectorPropertiesBuilder

-

-gogrid.propertiesbuilder=org.jclouds.gogrid.GoGridPropertiesBuilder

-gogrid.contextbuilder=org.jclouds.gogrid.GoGridContextBuilder

-

-ibmdev.propertiesbuilder=org.jclouds.ibmdev.IBMDeveloperCloudPropertiesBuilder

-ibmdev.contextbuilder=org.jclouds.ibmdev.IBMDeveloperCloudContextBuilder

-

-stub.propertiesbuilder=org.jclouds.compute.stub.StubComputeServicePropertiesBuilder

-stub.contextbuilder=org.jclouds.compute.stub.StubComputeServiceContextBuilder

-# example of where to change your endpoint

-# bluelock.endpoint=https://express3.bluelock.com/api

-

-atmosonline.contextbuilder=org.jclouds.atmosonline.saas.AtmosStorageContextBuilder

-atmosonline.endpoint=https://accesspoint.atmosonline.com

-atmosonline.apiversion=1.3.0

-

-synaptic.contextbuilder=org.jclouds.atmosonline.saas.AtmosStorageContextBuilder

-synaptic.endpoint=https://storage.synaptic.att.com

-synaptic.apiversion=1.3.0

-

-# TODO peer1 and hostedsolutions use atmos

-

-cloudfiles.contextbuilder=org.jclouds.rackspace.cloudfiles.CloudFilesContextBuilder

-cloudfiles.propertiesbuilder=org.jclouds.rackspace.cloudfiles.CloudFilesPropertiesBuilder

-

-walrus.contextbuilder=org.jclouds.aws.s3.S3ContextBuilder

-walrus.propertiesbuilder=org.jclouds.aws.s3.WalrusPropertiesBuilder

-

-googlestorage.contextbuilder=org.jclouds.aws.s3.S3ContextBuilder

-googlestorage.propertiesbuilder=org.jclouds.aws.s3.GoogleStoragePropertiesBuilder

-

-transient.contextbuilder=org.jclouds.blobstore.TransientBlobStoreContextBuilder

-transient.propertiesbuilder=org.jclouds.blobstore.TransientBlobStorePropertiesBuilder

-

-filesystem.contextbuilder=org.jclouds.filesystem.FilesystemBlobStoreContextBuilder

-filesystem.propertiesbuilder=org.jclouds.filesystem.FilesystemBlobStorePropertiesBuilder

-

diff --git a/core/src/test/java/org/jclouds/JcloudsVersionTest.java b/core/src/test/java/org/jclouds/JcloudsVersionTest.java
new file mode 100644
index 0000000..99acf27
--- /dev/null
+++ b/core/src/test/java/org/jclouds/JcloudsVersionTest.java
@@ -0,0 +1,139 @@
+/**
+ * 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;
+
+import static org.jclouds.JcloudsVersion.VERSION_RESOURCE_FILE;
+import static org.testng.Assert.*;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Andrew Phillips
+ */
+@Test(singleThreaded = true)
+public class JcloudsVersionTest {
+
+    @Test
+    public void testFailsIfResourceFileMissing() {
+        ClassLoader original = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(
+                new ResourceHidingClassLoader(original, VERSION_RESOURCE_FILE));
+        try {
+            new JcloudsVersion();
+            fail("Expected NullPointerException");
+        } catch (NullPointerException expected) {
+        } finally {
+            Thread.currentThread().setContextClassLoader(original);
+        }
+    }
+
+    @Test(expectedExceptions = { IllegalArgumentException.class })
+    public void testFailsIfInvalidVersion() {
+        new JcloudsVersion("${project.version}");
+    }
+
+    @Test
+    public void testExtractsVersionFromResourceFile() {
+        JcloudsVersion version = new JcloudsVersion();
+        assertEquals("0.0.0-SNAPSHOT", version.toString());
+    }
+
+    @Test
+    public void testExtractsMajorMinorPatchVersions() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3");
+        assertEquals(1, version.majorVersion);
+        assertEquals(2, version.minorVersion);
+        assertEquals(3, version.patchVersion);
+    }
+
+    @Test
+    public void testSupportsNonSnapshot() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3");
+        assertFalse(version.snapshot, "Expected non-snapshot");
+    }
+
+    @Test
+    public void testRecognisesSnapshot() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3-SNAPSHOT");
+        assertTrue(version.snapshot, "Expected snapshot");
+    }
+
+    @Test
+    public void testSupportsNonReleaseCandidate() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3");
+        assertFalse(version.releaseCandidate, "Expected non-release candidate");
+        assertNull(version.releaseCandidateVersion);
+    }
+
+    @Test
+    public void testRecognisesReleaseCandidate() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3-rc.4");
+        assertTrue(version.releaseCandidate, "Expected release candidate");
+    }
+
+    // TODO: remove once x.y.z-rc-n support is dropped after 1.3.0
+    @Test
+    public void testRecognisesNonSemverReleaseCandidate() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3-rc-4");
+        assertTrue(version.releaseCandidate, "Expected release candidate");
+    }
+
+    @Test
+    public void testExtractsReleaseCandidateVersion() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3-rc.4");
+        assertEquals(Integer.valueOf(4), version.releaseCandidateVersion);
+    }
+
+    // TODO: remove once x.y.z-rc-n support is dropped after 1.3.0
+    @Test
+    public void testExtractsNonSemverReleaseCandidateVersion() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3-rc-4");
+        assertEquals(Integer.valueOf(4), version.releaseCandidateVersion);
+    }
+
+    @Test
+    public void testRecognisesReleaseCandidateSnapshot() {
+        JcloudsVersion version = new JcloudsVersion("1.2.3-rc-4-SNAPSHOT");
+        assertTrue(version.releaseCandidate, "Expected release candidate");
+        assertTrue(version.snapshot, "Expected snapshot");
+    }
+
+    private static class ResourceHidingClassLoader extends ClassLoader {
+        private final ClassLoader delegate;
+        private final List<String> resourcesToHide;
+
+        private ResourceHidingClassLoader(ClassLoader delegate, String... resourcesToHide) {
+            this.delegate = delegate;
+            this.resourcesToHide = ImmutableList.copyOf(resourcesToHide);
+        }
+
+        @Override
+        public InputStream getResourceAsStream(String name) {
+            return (Iterables.contains(resourcesToHide, name)
+                    ? null
+                    : delegate.getResourceAsStream(name));
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/domain/JsonBallTest.java b/core/src/test/java/org/jclouds/domain/JsonBallTest.java
index 7bfa390..ffc1e82 100644
--- a/core/src/test/java/org/jclouds/domain/JsonBallTest.java
+++ b/core/src/test/java/org/jclouds/domain/JsonBallTest.java
@@ -54,7 +54,7 @@
 
    }
 
-   public void testHash() {
+   public void testObject() {
       String json = "{\"tomcat6\":{\"ssl_port\":8433}}";
 
       Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("tomcat6", new JsonBall("{\"ssl_port\":8433}"));
@@ -85,9 +85,20 @@
    }
 
    public void testNumber() {
-      String json = "{\"number\":1}";
+      String json = "{\"number\":1.0}";
 
-      Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("number", new JsonBall("1"));
+      Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("number", new JsonBall(1.0));
+
+      assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
+      assertEquals(mapper.toJson(map), json);
+
+   }
+
+
+   public void testBoolean() {
+      String json = "{\"boolean\":false}";
+
+      Map<String, JsonBall> map = ImmutableMap.<String, JsonBall> of("boolean", new JsonBall(false));
 
       assertEquals(handler.apply(new HttpResponse(200, "ok", Payloads.newStringPayload(json))), map);
       assertEquals(mapper.toJson(map), json);
diff --git a/core/src/test/java/org/jclouds/http/BaseJettyTest.java b/core/src/test/java/org/jclouds/http/BaseJettyTest.java
index 90ba02b..3c09fd1 100644
--- a/core/src/test/java/org/jclouds/http/BaseJettyTest.java
+++ b/core/src/test/java/org/jclouds/http/BaseJettyTest.java
@@ -45,12 +45,13 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.HttpHeaders;
 
+import org.eclipse.jetty.http.ssl.SslContextFactory;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.server.ssl.SslSocketConnector;
+import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
 import org.jclouds.Constants;
 import org.jclouds.crypto.CryptoStreams;
 import org.jclouds.io.InputSuppliers;
@@ -253,14 +254,18 @@
 
       server2 = new Server();
       server2.setHandler(server2Handler);
-      SslSocketConnector ssl = new SslSocketConnector();
-      ssl.setPort(testPort + 1);
-      ssl.setMaxIdleTime(30000);
-      ssl.setKeystore("src/test/resources/test.jks");
-      ssl.setKeyPassword("jclouds");
-      ssl.setTruststore("src/test/resources/test.jks");
-      ssl.setTrustPassword("jclouds");
-      server2.setConnectors(new Connector[] { ssl });
+
+      SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector();
+      ssl_connector.setPort(testPort + 1);
+      ssl_connector.setMaxIdleTime(30000);
+      SslContextFactory ssl = ssl_connector.getSslContextFactory();
+      ssl.setKeyStorePath("src/test/resources/test.jks");
+      ssl.setKeyStorePassword("jclouds");
+      ssl.setTrustStore("src/test/resources/test.jks");
+      ssl.setTrustStorePassword("jclouds");
+
+      server2.setConnectors(new Connector[]{  ssl_connector });
+     
       server2.start();
    }
 
@@ -282,7 +287,7 @@
       properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
       properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec("test",
-               "http://localhost:" + testPort, "1", "", "identity", null, IntegrationTestClient.class,
+               "http://localhost:" + testPort, "1", "", "", "identity", null, IntegrationTestClient.class,
                IntegrationTestAsyncClient.class, ImmutableSet.<Module> copyOf(connectionModules));
       return createContextBuilder(contextSpec, properties);
    }
@@ -328,12 +333,10 @@
 
    protected boolean failIfNoContentLength(HttpServletRequest request, HttpServletResponse response) throws IOException {
       Multimap<String, String> realHeaders = LinkedHashMultimap.create();
-      @SuppressWarnings("rawtypes")
-      Enumeration headers = request.getHeaderNames();
+      Enumeration<String> headers = request.getHeaderNames();
       while (headers.hasMoreElements()) {
          String header = headers.nextElement().toString();
-         @SuppressWarnings("rawtypes")
-         Enumeration values = request.getHeaders(header);
+         Enumeration<String> values = request.getHeaders(header);
          while (values.hasMoreElements()) {
             realHeaders.put(header, values.nextElement().toString());
          }
diff --git a/core/src/test/java/org/jclouds/http/IntegrationTestAsyncClient.java b/core/src/test/java/org/jclouds/http/IntegrationTestAsyncClient.java
index 50d516d..694c298 100644
--- a/core/src/test/java/org/jclouds/http/IntegrationTestAsyncClient.java
+++ b/core/src/test/java/org/jclouds/http/IntegrationTestAsyncClient.java
@@ -48,6 +48,7 @@
 import org.jclouds.rest.binders.BindMapToMatrixParams;
 import org.jclouds.rest.binders.BindToJsonPayload;
 import org.jclouds.rest.binders.BindToStringPayload;
+import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
 import org.jclouds.util.Strings2;
 
 import com.google.common.base.Function;
@@ -73,6 +74,7 @@
 
    @HEAD
    @Path("/objects/{id}")
+   @ExceptionParser(ReturnFalseOnNotFoundOr404.class)
    ListenableFuture<Boolean> exists(@PathParam("id") String path);
 
    @GET
diff --git a/core/src/test/java/org/jclouds/http/IntegrationTestClientExpectTest.java b/core/src/test/java/org/jclouds/http/IntegrationTestClientExpectTest.java
new file mode 100644
index 0000000..d584ef7
--- /dev/null
+++ b/core/src/test/java/org/jclouds/http/IntegrationTestClientExpectTest.java
@@ -0,0 +1,57 @@
+/**
+ * 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.http;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.rest.BaseRestClientExpectTest;
+import org.jclouds.rest.BaseRestClientExpectTest.RegisterContext;
+import org.testng.annotations.Test;
+
+/**
+ * 
+ * Allows us to test a client via its side effects.
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "IntegrationTestClientExpectTest")
+// only needed as IntegrationTestClient is not registered in rest.properties
+@RegisterContext(sync = IntegrationTestClient.class, async = IntegrationTestAsyncClient.class)
+public class IntegrationTestClientExpectTest extends BaseRestClientExpectTest<IntegrationTestClient> {
+
+   public void testWhenResponseIs2xxExistsReturnsTrue() {
+
+      IntegrationTestClient client = requestSendsResponse(HttpRequest.builder().method("HEAD").endpoint(
+               URI.create("http://mock/objects/rabbit")).build(), HttpResponse.builder().statusCode(200).build());
+
+      assertEquals(client.exists("rabbit"), true);
+
+   }
+
+   public void testWhenResponseIs404ExistsReturnsFalse() {
+
+      IntegrationTestClient client = requestSendsResponse(HttpRequest.builder().method("HEAD").endpoint(
+               URI.create("http://mock/objects/rabbit")).build(), HttpResponse.builder().statusCode(404).build());
+
+      assertEquals(client.exists("rabbit"), false);
+
+   }
+}
diff --git a/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java b/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java
index b672608..db0dba6 100644
--- a/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java
+++ b/core/src/test/java/org/jclouds/http/internal/TrackingJavaUrlHttpCommandExecutorService.java
@@ -1,3 +1,21 @@
+/**
+ * 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.http.internal;
 
 import java.lang.reflect.Method;
diff --git a/core/src/test/java/org/jclouds/json/internal/NullHackJsonLiteralAdapterTest.java b/core/src/test/java/org/jclouds/json/internal/NullHackJsonLiteralAdapterTest.java
new file mode 100644
index 0000000..1a236ee
--- /dev/null
+++ b/core/src/test/java/org/jclouds/json/internal/NullHackJsonLiteralAdapterTest.java
@@ -0,0 +1,169 @@
+/**
+ * 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.json.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
+import org.jclouds.util.Patterns;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Shows how we currently allow users to specify json literal types.
+ * 
+ * @author Adrian Cole
+ * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/>
+ */
+@Test(testName = "NullHackJsonLiteralAdapterTest")
+public class NullHackJsonLiteralAdapterTest {
+
+   /**
+    * User supplied type that holds json literally. Ex. number as {@code 8}, boolean as {@code true}
+    * , string as {@code "value"}, object as {@code , list {@code []}.
+    */
+   static class RawJson implements CharSequence {
+
+      private final String value;
+
+      public RawJson(double value) {
+         this.value = value + "";
+      }
+
+      public RawJson(boolean value) {
+         this.value = value + "";
+      }
+
+      public RawJson(String value) {
+         this.value = quoteStringIfNotNumberOrBoolean(checkNotNull(value, "value"));
+      }
+
+      @Override
+      public int hashCode() {
+         return Objects.hashCode(value);
+      }
+
+      @Override
+      public boolean equals(Object that) {
+         if (that == null)
+            return false;
+         return Objects.equal(this.toString(), that.toString());
+      }
+
+      @Override
+      public String toString() {
+         return value;
+      }
+
+      static String quoteStringIfNotNumberOrBoolean(String in) {
+         if (Patterns.JSON_STRING_PATTERN.matcher(in).find() && !Patterns.JSON_NUMBER_PATTERN.matcher(in).find()
+                  && !Patterns.JSON_BOOLEAN_PATTERN.matcher(in).find()) {
+            return "\"" + in + "\"";
+         }
+         return in;
+      }
+
+      @Override
+      public char charAt(int index) {
+         return value.charAt(index);
+      }
+
+      @Override
+      public int length() {
+         return value.length();
+      }
+
+      @Override
+      public CharSequence subSequence(int start, int end) {
+         return value.subSequence(start, end);
+      }
+
+   }
+
+   /**
+    * writes or reads the literal directly
+    */
+   public static class RawJsonAdapter extends NullHackJsonLiteralAdapter<RawJson> {
+
+      @Override
+      protected RawJson createJsonLiteralFromRawJson(String json) {
+         return new RawJson(json);
+      }
+  }
+
+   // register the type adapter so that gson can serialize/deserialize to it
+   private Gson gsonAdapter = new GsonBuilder().registerTypeAdapter(RawJson.class, new RawJsonAdapter()).create();
+
+   Type type = new TypeToken<Map<String, RawJson>>() {
+   }.getType();
+
+   public void testObject() {
+      String json = "{\"tomcat6\":{\"ssl_port\":8433}}";
+
+      Map<String, RawJson> map = ImmutableMap.<String, RawJson> of("tomcat6", new RawJson("{\"ssl_port\":8433}"));
+
+      assertEquals(gsonAdapter.toJson(map), json);
+      assertEquals(gsonAdapter.fromJson(json, type), map);
+   }
+
+   public void testList() {
+      String json = "{\"list\":[8431,8433]}";
+
+      Map<String, RawJson> map = ImmutableMap.<String, RawJson> of("list", new RawJson("[8431,8433]"));
+
+      assertEquals(gsonAdapter.toJson(map), json);
+      assertEquals(gsonAdapter.fromJson(json, type), map);
+   }
+
+   public void testString() {
+      String json = "{\"name\":\"fooy\"}";
+
+      Map<String, RawJson> map = ImmutableMap.<String, RawJson> of("name", new RawJson("fooy"));
+
+      assertEquals(gsonAdapter.toJson(map), json);
+      assertEquals(gsonAdapter.fromJson(json, type), map);
+   }
+
+   public void testNumber() {
+      String json = "{\"number\":1.0}";
+
+      Map<String, RawJson> map = ImmutableMap.<String, RawJson> of("number", new RawJson(1.0));
+
+      assertEquals(gsonAdapter.toJson(map), json);
+      assertEquals(gsonAdapter.fromJson(json, type), map);
+   }
+
+   public void testBoolean() {
+      String json = "{\"boolean\":false}";
+
+      Map<String, RawJson> map = ImmutableMap.<String, RawJson> of("boolean", new RawJson(false));
+
+      assertEquals(gsonAdapter.toJson(map), json);
+      assertEquals(gsonAdapter.fromJson(json, type), map);
+   }
+}
diff --git a/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java b/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java
index d31f217..7bdf751 100644
--- a/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java
+++ b/core/src/test/java/org/jclouds/lifecycle/config/LifeCycleModuleTest.java
@@ -27,6 +27,8 @@
 import org.jclouds.Constants;
 import org.jclouds.concurrent.config.ExecutorServiceModule;
 import org.jclouds.lifecycle.Closer;
+
+import com.google.common.util.concurrent.ExecutionList;
 import com.google.inject.name.Names;
 import org.testng.annotations.Test;
 
@@ -75,6 +77,9 @@
             return 1;
          }
       }, new ExecutorServiceModule());
+      // TODO: currently have to manually invoke the execution list, as otherwise it may occur
+      // before everything is wired up
+      i.getInstance(ExecutionList.class).execute();
       return i;
    }
 
diff --git a/core/src/test/java/org/jclouds/logging/BufferLogger.java b/core/src/test/java/org/jclouds/logging/BufferLogger.java
index 9618c01..083b92b 100644
--- a/core/src/test/java/org/jclouds/logging/BufferLogger.java
+++ b/core/src/test/java/org/jclouds/logging/BufferLogger.java
@@ -1,3 +1,21 @@
+/**
+ * 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.logging;
 
 import java.util.ArrayList;
diff --git a/core/src/test/java/org/jclouds/logging/BufferLoggerTest.java b/core/src/test/java/org/jclouds/logging/BufferLoggerTest.java
index b054c8d..2954c8c 100644
--- a/core/src/test/java/org/jclouds/logging/BufferLoggerTest.java
+++ b/core/src/test/java/org/jclouds/logging/BufferLoggerTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.logging;
 
 import java.util.logging.Level;
diff --git a/core/src/test/java/org/jclouds/rest/BaseRestClientExpectTest.java b/core/src/test/java/org/jclouds/rest/BaseRestClientExpectTest.java
new file mode 100644
index 0000000..61b54f2
--- /dev/null
+++ b/core/src/test/java/org/jclouds/rest/BaseRestClientExpectTest.java
@@ -0,0 +1,391 @@
+/**
+ * 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.rest;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.jclouds.rest.RestContextFactory.contextSpec;
+import static org.jclouds.rest.RestContextFactory.createContext;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.concurrent.MoreExecutors;
+import org.jclouds.concurrent.SingleThreaded;
+import org.jclouds.concurrent.config.ConfiguresExecutorService;
+import org.jclouds.http.HttpCommandExecutorService;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpUtils;
+import org.jclouds.http.IOExceptionRetryHandler;
+import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
+import org.jclouds.http.handlers.DelegatingErrorHandler;
+import org.jclouds.http.handlers.DelegatingRetryHandler;
+import org.jclouds.http.internal.BaseHttpCommandExecutorService;
+import org.jclouds.http.internal.HttpWire;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.logging.config.NullLoggingModule;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.Test;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Binder;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+
+/**
+ * 
+ * Allows us to test a client via its side effects.
+ * 
+ * <p/>
+ * Example usage:
+ * 
+ * <pre>
+ * 
+ * HttpRequest bucketFooExists = HttpRequest.builder().method(&quot;HEAD&quot;).endpoint(
+ *          URI.create(&quot;https://foo.s3.amazonaws.com/?max-keys=0&quot;)).headers(
+ *          ImmutableMultimap.&lt;String, String&gt; builder().put(&quot;Host&quot;, &quot;foo.s3.amazonaws.com&quot;).put(&quot;Date&quot;, CONSTANT_DATE)
+ *                   .put(&quot;Authorization&quot;, &quot;AWS identity:86P4BBb7xT+gBqq7jxM8Tc28ktY=&quot;).build()).build();
+ * 
+ * S3Client clientWhenBucketExists = requestSendsResponse(bucketFooExists, HttpResponse.builder().statusCode(200).build());
+ * assert clientWhenBucketExists.bucketExists(&quot;foo&quot;);
+ * 
+ * S3Client clientWhenBucketDoesntExist = requestSendsResponse(bucketFooExists, HttpResponse.builder().statusCode(404)
+ *          .build());
+ * assert !clientWhenBucketDoesntExist.bucketExists(&quot;foo&quot;);
+ * </pre>
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+@Beta
+public abstract class BaseRestClientExpectTest<S> {
+   /**
+    * only needed when the client is simple and not registered fn rest.properties
+    */
+   @Target(TYPE)
+   @Retention(RUNTIME)
+   public static @interface RegisterContext {
+      Class<?> sync();
+
+      Class<?> async();
+   }
+
+   protected String provider = "mock";
+
+   /**
+    * Override this to supply alternative bindings for use in the test. This is commonly used to
+    * override suppliers of dates so that the test results are predicatable.
+    * 
+    * @return optional guice module which can override bindings
+    */
+   protected Module createModule() {
+      return new Module() {
+
+         @Override
+         public void configure(Binder binder) {
+
+         }
+
+      };
+   }
+
+   /**
+    * Convenience method used when creating a response that includes an http payload.
+    * 
+    * <p/>
+    * ex.
+    * 
+    * <pre>
+    * HttpResponse.builder().statusCode(200).payload(payloadFromResource(&quot;/ip_get_details.json&quot;)).build()
+    * </pre>
+    * 
+    * @param resource
+    *           resource file such as {@code /serverlist.json}
+    * @return payload for use in http responses.
+    */
+   public Payload payloadFromResource(String resource) {
+      return Payloads.newInputStreamPayload(getClass().getResourceAsStream(resource));
+   }
+
+   /**
+    * Mock executor service which uses the supplied function to return http responses.
+    */
+   @SingleThreaded
+   @Singleton
+   public static class ExpectHttpCommandExecutorService extends BaseHttpCommandExecutorService<HttpRequest> {
+
+      private final Function<HttpRequest, HttpResponse> fn;
+
+      @Inject
+      public ExpectHttpCommandExecutorService(Function<HttpRequest, HttpResponse> fn, HttpUtils utils,
+               @Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor,
+               IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler,
+               DelegatingErrorHandler errorHandler, HttpWire wire) {
+         super(utils, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
+         this.fn = checkNotNull(fn, "fn");
+      }
+
+      @Override
+      public void cleanup(HttpRequest nativeResponse) {
+         if (nativeResponse.getPayload() != null)
+            nativeResponse.getPayload().release();
+      }
+
+      @Override
+      public HttpRequest convert(HttpRequest request) throws IOException, InterruptedException {
+         return request;
+      }
+
+      @Override
+      public HttpResponse invoke(HttpRequest nativeRequest) throws IOException, InterruptedException {
+         return fn.apply(nativeRequest);
+      }
+   }
+
+   @ConfiguresHttpCommandExecutorService
+   @ConfiguresExecutorService
+   public static class ExpectModule extends AbstractModule {
+      private final Function<HttpRequest, HttpResponse> fn;
+
+      public ExpectModule(Function<HttpRequest, HttpResponse> fn) {
+         this.fn = checkNotNull(fn, "fn");
+      }
+
+      @Override
+      public void configure() {
+         bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_USER_THREADS)).toInstance(
+                  MoreExecutors.sameThreadExecutor());
+         bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).toInstance(
+                  MoreExecutors.sameThreadExecutor());
+         bind(new TypeLiteral<Function<HttpRequest, HttpResponse>>() {
+         }).toInstance(fn);
+         bind(HttpCommandExecutorService.class).to(ExpectHttpCommandExecutorService.class);
+      }
+   }
+
+   /**
+    * creates a client for a mock server which only responds to a single http request
+    * 
+    * @param request
+    *           the http request the mock server responds to
+    * @param response
+    *           the response the mock server returns for the request
+    * @return a client configured with this behavior
+    */
+   public S requestSendsResponse(HttpRequest request, HttpResponse response) {
+      return requestSendsResponse(request, response, createModule());
+   }
+   
+   public S requestSendsResponse(HttpRequest request, HttpResponse response, Module module) {
+      return requestsSendResponses(ImmutableMap.of(request, response), module);
+   }
+   
+   /**
+    * creates a client for a mock server which only responds to two types of requests
+    * 
+    * @param requestA
+    *           an http request the mock server responds to
+    * @param responseA
+    *           the response for {@code requestA}
+    * @param requestB
+    *           another http request the mock server responds to
+    * @param responseB
+    *           the response for {@code requestB}
+    * @return a client configured with this behavior
+    */
+   public S requestsSendResponses(HttpRequest requestA, HttpResponse responseA, HttpRequest requestB,
+            HttpResponse responseB) {
+      return requestsSendResponses(requestA, responseA, requestB, responseB, createModule());
+   }
+
+   public S requestsSendResponses(HttpRequest requestA, HttpResponse responseA, HttpRequest requestB,
+            HttpResponse responseB, Module module) {
+      return requestsSendResponses(ImmutableMap.of(requestA, responseA, requestB, responseB), module);
+   }
+
+   /**
+    * creates a client for a mock server which only responds to three types of requests
+    * 
+    * @param requestA
+    *           an http request the mock server responds to
+    * @param responseA
+    *           the response for {@code requestA}
+    * @param requestB
+    *           another http request the mock server responds to
+    * @param responseB
+    *           the response for {@code requestB}
+    * @param requestC
+    *           another http request the mock server responds to
+    * @param responseC
+    *           the response for {@code requestC}
+    * @return a client configured with this behavior
+    */
+   public S requestsSendResponses(HttpRequest requestA, HttpResponse responseA, HttpRequest requestB,
+            HttpResponse responseB, HttpRequest requestC, HttpResponse responseC) {
+      return requestsSendResponses(requestA, responseA, requestB, responseB, requestC, responseC, createModule());
+   }
+   
+   public S requestsSendResponses(HttpRequest requestA, HttpResponse responseA, HttpRequest requestB,
+            HttpResponse responseB, HttpRequest requestC, HttpResponse responseC, Module module) {
+      return requestsSendResponses(ImmutableMap.of(requestA, responseA, requestB, responseB, requestC, responseC), module);
+   }
+
+   public S orderedRequestsSendResponses(HttpRequest requestA, HttpResponse responseA) {
+      return orderedRequestsSendResponses(ImmutableList.of(requestA), ImmutableList.of(responseA));
+   }
+
+   public S orderedRequestsSendResponses(HttpRequest requestA, HttpResponse responseA, HttpRequest requestB,
+            HttpResponse responseB) {
+      return orderedRequestsSendResponses(ImmutableList.of(requestA, requestB), ImmutableList.of(responseA, responseB));
+   }
+
+   public S orderedRequestsSendResponses(HttpRequest requestA, HttpResponse responseA, HttpRequest requestB,
+            HttpResponse responseB, HttpRequest requestC, HttpResponse responseC) {
+      return orderedRequestsSendResponses(ImmutableList.of(requestA, requestB, requestC), ImmutableList.of(responseA, responseB, responseC));
+   }
+
+   public S orderedRequestsSendResponses(HttpRequest requestA, HttpResponse responseA, HttpRequest requestB,
+            HttpResponse responseB, HttpRequest requestC, HttpResponse responseC, HttpRequest requestD, HttpResponse responseD) {
+      return orderedRequestsSendResponses(ImmutableList.of(requestA, requestB, requestC, requestD), ImmutableList.of(responseA, responseB, responseC, responseD));
+   }
+
+   public S orderedRequestsSendResponses(final List<HttpRequest> requests, final List<HttpResponse> responses) {
+      final AtomicInteger counter = new AtomicInteger(0);
+      
+      return createClient(new Function<HttpRequest,HttpResponse>() {
+         @Override
+         public HttpResponse apply(HttpRequest input) {
+            int index = counter.getAndIncrement();
+            if (index >= requests.size()) {
+               throw new IndexOutOfBoundsException("request index "+index+", but only "+requests.size()+" request/response pairs");
+            }
+            assert input.equals(requests.get(index)) : "expected="+requests.get(index)+"; actual="+input;
+            return responses.get(index);
+         }
+      });
+   }
+
+   /**
+    * creates a client for a mock server which returns responses for requests based on the supplied
+    * Map parameter.
+    * 
+    * @param requestToResponse
+    *           valid requests and responses for the mock to respond to
+    * @return a client configured with this behavior
+    */
+   public S requestsSendResponses(Map<HttpRequest, HttpResponse> requestToResponse) {
+      return requestsSendResponses(requestToResponse, createModule());
+   }
+
+   public S requestsSendResponses(Map<HttpRequest, HttpResponse> requestToResponse, Module module) {
+      return createClient(Functions.forMap(requestToResponse), module);
+   }
+
+   public String renderRequest(HttpRequest request) {
+      StringBuilder builder = new StringBuilder().append(request.getRequestLine()).append('\n');
+      for (Entry<String, String> header : request.getHeaders().entries()) {
+         builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
+      }
+      if (request.getPayload() != null) {
+         for (Entry<String, String> header : HttpUtils.getContentHeadersFromMetadata(
+                  request.getPayload().getContentMetadata()).entries()) {
+            builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
+         }
+         try {
+            builder.append('\n').append(Strings2.toStringAndClose(request.getPayload().getInput()));
+         } catch (IOException e) {
+            throw Throwables.propagate(e);
+         }
+
+      } else {
+         builder.append('\n');
+      }
+      return builder.toString();
+   }
+
+   public S createClient(Function<HttpRequest, HttpResponse> fn) {
+      return createClient(fn, createModule(), setupProperties());
+   }
+
+   public S createClient(Function<HttpRequest, HttpResponse> fn, Module module) {
+      return createClient(fn, module, setupProperties());
+
+   }
+
+   public S createClient(Function<HttpRequest, HttpResponse> fn, Properties props) {
+      return createClient(fn, createModule(), props);
+
+   }
+
+   public S createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
+      RestContextSpec<S, ?> contextSpec = makeContextSpec();
+
+      return createContext(contextSpec,
+               ImmutableSet.<Module> of(new ExpectModule(fn), new NullLoggingModule(), module), props).getApi();
+   }
+
+   @SuppressWarnings("unchecked")
+   private RestContextSpec<S, ?> makeContextSpec() {
+      if (getClass().isAnnotationPresent(RegisterContext.class))
+         return (RestContextSpec<S, ?>) contextSpec(provider, "http://mock", "1", "", "", "userfoo", null, getClass()
+                  .getAnnotation(RegisterContext.class).sync(),
+                  getClass().getAnnotation(RegisterContext.class).async(), ImmutableSet.<Module> of());
+      else
+         return new RestContextFactory(setupRestProperties()).createContextSpec(provider, "identity", "credential",
+                  new Properties());
+   }
+
+   /**
+    * override this when the provider or api is not located in rest.properties and you are not using
+    * the {@link RegisterContext} annotation on your tests.
+    */
+   protected Properties setupRestProperties() {
+      return RestContextFactory.getPropertiesFromResource("/rest.properties");
+   }
+
+   /**
+    * override this to supply context-specific parameters during tests.
+    */
+   protected Properties setupProperties() {
+      return new Properties();
+   }
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/rest/BaseRestClientLiveTest.java b/core/src/test/java/org/jclouds/rest/BaseRestClientLiveTest.java
new file mode 100644
index 0000000..c4a787d
--- /dev/null
+++ b/core/src/test/java/org/jclouds/rest/BaseRestClientLiveTest.java
@@ -0,0 +1,82 @@
+/**
+ * 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.rest;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.emptyToNull;
+
+import java.util.Properties;
+
+import org.jclouds.Constants;
+import org.testng.annotations.BeforeClass;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public abstract class BaseRestClientLiveTest {
+   protected String prefix = System.getProperty("user.name");
+
+   protected String provider;
+   protected String identity;
+   protected String credential;
+   protected String endpoint;
+   protected String apiVersion;
+   protected String buildVersion;
+
+
+   protected Properties setupRestProperties() {
+      return RestContextFactory.getPropertiesFromResource("/rest.properties");
+   }
+
+   protected Properties setupProperties() {
+
+      if (emptyToNull(provider) == null)
+         throw new NullPointerException("provider must not be null or empty:" + provider);
+      if (emptyToNull(identity) == null)
+         throw new NullPointerException("identity must not be null or empty:" + provider);
+
+      Properties overrides = new Properties();
+      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
+      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
+
+      overrides.setProperty(provider + ".identity", identity);
+
+      if (credential != null)
+         overrides.setProperty(provider + ".credential", credential);
+      if (endpoint != null)
+         overrides.setProperty(provider + ".endpoint", endpoint);
+      if (apiVersion != null)
+         overrides.setProperty(provider + ".api-version", apiVersion);
+      if (buildVersion != null)
+         overrides.setProperty(provider + ".build-version", buildVersion);
+
+      return overrides;
+   }
+
+   @BeforeClass
+   protected void setupCredentials() {
+      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
+      credential = System.getProperty("test." + provider + ".credential");
+      endpoint = System.getProperty("test." + provider + ".endpoint");
+      apiVersion = System.getProperty("test." + provider + ".api-version");
+      buildVersion = System.getProperty("test." + provider + ".build-version");
+   }
+
+}
diff --git a/core/src/test/java/org/jclouds/rest/BaseRestClientTest.java b/core/src/test/java/org/jclouds/rest/BaseRestClientTest.java
index b307a22..b052b05 100644
--- a/core/src/test/java/org/jclouds/rest/BaseRestClientTest.java
+++ b/core/src/test/java/org/jclouds/rest/BaseRestClientTest.java
@@ -35,8 +35,8 @@
 import org.jclouds.concurrent.config.ConfiguresExecutorService;
 import org.jclouds.crypto.Crypto;
 import org.jclouds.crypto.CryptoStreams;
+import org.jclouds.http.HttpCommandExecutorService;
 import org.jclouds.http.HttpRequest;
-import org.jclouds.http.TransformingHttpCommandExecutorService;
 import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
 import org.jclouds.http.functions.ParseSax;
 import org.jclouds.io.MutableContentMetadata;
@@ -65,13 +65,13 @@
    @ConfiguresHttpCommandExecutorService
    @ConfiguresExecutorService
    public static class MockModule extends AbstractModule {
-      private final TransformingHttpCommandExecutorService mock;
+      private final HttpCommandExecutorService mock;
 
       public MockModule() {
-         this(createMock(TransformingHttpCommandExecutorService.class));
+         this(createMock(HttpCommandExecutorService.class));
       }
 
-      public MockModule(TransformingHttpCommandExecutorService mock) {
+      public MockModule(HttpCommandExecutorService mock) {
          this.mock = mock;
       }
 
@@ -81,7 +81,7 @@
                MoreExecutors.sameThreadExecutor());
          bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).toInstance(
                MoreExecutors.sameThreadExecutor());
-         bind(TransformingHttpCommandExecutorService.class).toInstance(mock);
+         bind(HttpCommandExecutorService.class).toInstance(mock);
       }
    }
 
diff --git a/core/src/test/java/org/jclouds/rest/InputParamValidatorTest.java b/core/src/test/java/org/jclouds/rest/InputParamValidatorTest.java
index 52bc193..108f14f 100644
--- a/core/src/test/java/org/jclouds/rest/InputParamValidatorTest.java
+++ b/core/src/test/java/org/jclouds/rest/InputParamValidatorTest.java
@@ -141,7 +141,7 @@
    void setupFactory() {
 
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec("test",
-               "http://localhost:9999", "1", "", "userFoo", null, IntegrationTestClient.class,
+               "http://localhost:9999", "1", "", "", "userFoo", null, IntegrationTestClient.class,
                IntegrationTestAsyncClient.class);
 
       injector = createContextBuilder(contextSpec).buildInjector();
diff --git a/core/src/test/java/org/jclouds/rest/RestClientTest.java b/core/src/test/java/org/jclouds/rest/RestClientTest.java
index c4bf0d7..627f07d 100644
--- a/core/src/test/java/org/jclouds/rest/RestClientTest.java
+++ b/core/src/test/java/org/jclouds/rest/RestClientTest.java
@@ -65,6 +65,7 @@
    @BeforeClass
    protected void setupFactory() throws IOException {
       RestContextSpec<?, ?> contextSpec = createContextSpec();
+      
       injector = createContextBuilder(contextSpec,
             ImmutableSet.of(new MockModule(), new NullLoggingModule(), createModule()), getProperties())
             .buildInjector();
diff --git a/core/src/test/java/org/jclouds/rest/RestContextFactoryTest.java b/core/src/test/java/org/jclouds/rest/RestContextFactoryTest.java
index 0a1c31f..3dcfdd8 100644
--- a/core/src/test/java/org/jclouds/rest/RestContextFactoryTest.java
+++ b/core/src/test/java/org/jclouds/rest/RestContextFactoryTest.java
@@ -53,7 +53,7 @@
 
    public void testBuilder() {
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec(provider,
-               "http://localhost", "1", "", "dummy", null, IntegrationTestClient.class,
+               "http://localhost", "1", "", "", "dummy", null, IntegrationTestClient.class,
                IntegrationTestAsyncClient.class);
 
       createContextBuilder(contextSpec);
@@ -61,12 +61,13 @@
 
    public void testBuilderPropertiesWithIso3166() {
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec(provider,
-               "http://localhost", "1", "US-CA", "dummy", null, IntegrationTestClient.class,
+               "http://localhost", "1", "build-foo", "US-CA", "dummy", null, IntegrationTestClient.class,
                IntegrationTestAsyncClient.class);
 
       Properties props = RestContextFactory.toProperties(contextSpec);
       assertEquals(props.getProperty("test.endpoint"), "http://localhost");
-      assertEquals(props.getProperty("test.apiversion"), "1");
+      assertEquals(props.getProperty("test.api-version"), "1");
+      assertEquals(props.getProperty("test.build-version"), "build-foo");
       assertEquals(props.getProperty("test.identity"), "dummy");
       assertEquals(props.getProperty("test.iso3166-codes"), "US-CA");
       assertEquals(props.getProperty("test.credential"), null);
@@ -81,12 +82,13 @@
 
    public void testBuilderPropertiesWithCredential() {
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec(provider,
-               "http://localhost", "1", "", "dummy", "credential", IntegrationTestClient.class,
+               "http://localhost", "1", "build-foo", "", "dummy", "credential", IntegrationTestClient.class,
                IntegrationTestAsyncClient.class);
 
       Properties props = RestContextFactory.toProperties(contextSpec);
       assertEquals(props.getProperty("test.endpoint"), "http://localhost");
-      assertEquals(props.getProperty("test.apiversion"), "1");
+      assertEquals(props.getProperty("test.api-version"), "1");
+      assertEquals(props.getProperty("test.build-version"), "build-foo");
       assertEquals(props.getProperty("test.identity"), "dummy");
       assertEquals(props.getProperty("test.credential"), "credential");
       assertEquals(props.getProperty("test.sync"), IntegrationTestClient.class.getName());
@@ -102,12 +104,13 @@
    public void testBuilderPropertiesWithContextBuilder() {
       @SuppressWarnings("rawtypes")
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec(provider,
-               "http://localhost", "1", "", "dummy", null, (Class) null, (Class) null, PropertiesBuilder.class,
+               "http://localhost", "1", "build-foo", "", "dummy", null, (Class) null, (Class) null, PropertiesBuilder.class,
                (Class) IntegrationTestContextBuilder.class, Collections.EMPTY_LIST);
 
       Properties props = RestContextFactory.toProperties(contextSpec);
       assertEquals(props.getProperty("test.endpoint"), "http://localhost");
-      assertEquals(props.getProperty("test.apiversion"), "1");
+      assertEquals(props.getProperty("test.api-version"), "1");
+      assertEquals(props.getProperty("test.build-version"), "build-foo");
       assertEquals(props.getProperty("test.identity"), "dummy");
       assertEquals(props.getProperty("test.credential"), null);
       assertEquals(props.getProperty("test.sync"), null);
@@ -123,12 +126,13 @@
    public void testBuilderPropertiesWithModule() {
       @SuppressWarnings("rawtypes")
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec(provider,
-               "http://localhost", "1", "", "dummy", null, (Class) null, (Class) null, PropertiesBuilder.class,
+               "http://localhost", "1", "build-foo", "", "dummy", null, (Class) null, (Class) null, PropertiesBuilder.class,
                (Class) IntegrationTestContextBuilder.class, Collections.<Module> singleton(new A()));
 
       Properties props = RestContextFactory.toProperties(contextSpec);
       assertEquals(props.getProperty("test.endpoint"), "http://localhost");
-      assertEquals(props.getProperty("test.apiversion"), "1");
+      assertEquals(props.getProperty("test.api-version"), "1");
+      assertEquals(props.getProperty("test.build-version"), "build-foo");
       assertEquals(props.getProperty("test.identity"), "dummy");
       assertEquals(props.getProperty("test.credential"), null);
       assertEquals(props.getProperty("test.sync"), null);
@@ -144,12 +148,13 @@
    public void testBuilderPropertiesWithModules() {
       @SuppressWarnings("rawtypes")
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec(provider,
-               "http://localhost", "1", "", "dummy", null, (Class) null, (Class) null, PropertiesBuilder.class,
+               "http://localhost", "1", "build-foo", "", "dummy", null, (Class) null, (Class) null, PropertiesBuilder.class,
                (Class) IntegrationTestContextBuilder.class, Arrays.<Module> asList(new A(), new B()));
 
       Properties props = RestContextFactory.toProperties(contextSpec);
       assertEquals(props.getProperty("test.endpoint"), "http://localhost");
-      assertEquals(props.getProperty("test.apiversion"), "1");
+      assertEquals(props.getProperty("test.api-version"), "1");
+      assertEquals(props.getProperty("test.build-version"), "build-foo");
       assertEquals(props.getProperty("test.identity"), "dummy");
       assertEquals(props.getProperty("test.credential"), null);
       assertEquals(props.getProperty("test.sync"), null);
@@ -165,7 +170,8 @@
    public void testBuilderPropertiesJCloudsScope() {
       Properties props = new Properties();
       props.setProperty("test.endpoint", "http://localhost");
-      props.setProperty("test.apiversion", "1");
+      props.setProperty("test.api-version", "1");
+      props.setProperty("test.build-version", "build-foo");
       props.setProperty("test.iso3166-codes", "US");
       props.setProperty("jclouds.identity", "foo");
       props.setProperty("jclouds.credential", "bar");
@@ -186,6 +192,8 @@
             assertEquals(spec.iso3166Codes, "US");
             assertEquals(spec.identity, "foo");
             assertEquals(spec.credential, "bar");
+            assertEquals(spec.apiVersion, "1");
+            assertEquals(spec.buildVersion, "build-foo");
             assertEquals(Iterables.size(spec.modules), 2);
             return spec;
          }
@@ -200,7 +208,8 @@
       Files.write("bar", file, Charsets.UTF_8);
       Properties props = new Properties();
       props.setProperty("test.endpoint", "http://localhost");
-      props.setProperty("test.apiversion", "1");
+      props.setProperty("test.api-version", "1");
+      props.setProperty("test.build-version", "build-foo");
       props.setProperty("test.iso3166-codes", "US");
       props.setProperty("test.identity", "foo");
       props.setProperty("test.credential.file", file.getAbsolutePath());
@@ -221,6 +230,8 @@
             assertEquals(spec.iso3166Codes, "US");
             assertEquals(spec.identity, "foo");
             assertEquals(spec.credential, "bar");
+            assertEquals(spec.apiVersion, "1");
+            assertEquals(spec.buildVersion, "build-foo");
             assertEquals(Iterables.size(spec.modules), 2);
             return spec;
          }
@@ -251,7 +262,7 @@
    public void testBuilderPropertiesWithWrongConfig() {
       @SuppressWarnings( { "unused", "rawtypes" })
       RestContextSpec<IntegrationTestClient, IntegrationTestAsyncClient> contextSpec = contextSpec(provider,
-               "http://localhost", "1", "", "dummy", null, (Class) null, (Class) null,
+               "http://localhost", "1", "build-foo", "", "dummy", null, (Class) null, (Class) null,
                (Class) IntegrationTestContextBuilder.class, (Class) PropertiesBuilder.class, Collections.EMPTY_LIST);
    }
 
diff --git a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java
index 5f2bbdc..51607f8 100644
--- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java
+++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java
@@ -20,12 +20,6 @@
 
 import static com.google.common.base.Charsets.UTF_8;
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.reportMatcher;
-import static org.easymock.classextension.EasyMock.createNiceMock;
-import static org.easymock.classextension.EasyMock.replay;
-import static org.easymock.classextension.EasyMock.verify;
 import static org.jclouds.io.Payloads.calculateMD5;
 import static org.jclouds.io.Payloads.newInputStreamPayload;
 import static org.jclouds.io.Payloads.newStringPayload;
@@ -53,6 +47,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
 import javax.inject.Named;
@@ -75,26 +70,24 @@
 import javax.ws.rs.core.UriBuilder;
 import javax.xml.bind.annotation.XmlRootElement;
 
-import org.easymock.IArgumentMatcher;
 import org.eclipse.jetty.http.HttpHeaders;
 import org.jclouds.concurrent.Timeout;
 import org.jclouds.crypto.Crypto;
 import org.jclouds.date.DateService;
 import org.jclouds.date.internal.SimpleDateFormatDateService;
 import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpCommandExecutorService;
 import org.jclouds.http.HttpException;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpRequestFilter;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.http.IOExceptionRetryHandler;
 import org.jclouds.http.RequiresHttp;
-import org.jclouds.http.TransformingHttpCommandExecutorService;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
 import org.jclouds.http.functions.ParseJson;
 import org.jclouds.http.functions.ParseSax;
 import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
 import org.jclouds.http.functions.ParseXMLWithJAXB;
-import org.jclouds.http.functions.ReleasePayloadAndReturn;
 import org.jclouds.http.functions.ReturnInputStream;
 import org.jclouds.http.functions.ReturnStringIf2xx;
 import org.jclouds.http.functions.ReturnTrueIf2xx;
@@ -180,8 +173,8 @@
 
    @RequiresHttp
    @ConfiguresRestClient
-   protected static class CallerCalleeModule extends RestClientModule<Caller, AsyncCaller> {
-      CallerCalleeModule() {
+   protected static class CallerModule extends RestClientModule<Caller, AsyncCaller> {
+      CallerModule() {
          super(Caller.class, AsyncCaller.class, ImmutableMap.<Class<?>, Class<?>> of(Callee.class, AsyncCallee.class));
       }
 
@@ -227,13 +220,17 @@
       public AsyncCallee getCallee(@EndpointParam URI endpoint);
    }
 
-   @SuppressWarnings("unchecked")
-   public void testDelegateAsyncIncludesVersion() throws SecurityException, NoSuchMethodException,
-         InterruptedException, ExecutionException {
-      Injector child = injectorForClient();
-      TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class);
+   public void testAsyncDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, ExecutionException {
+      Injector child = injectorForCaller(new HttpCommandExecutorService() {
 
-      ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class);
+         @Override
+         public Future<HttpResponse> submit(HttpCommand command) {
+            assertEquals(command.getCurrentRequest().getRequestLine(),
+                     "GET http://localhost:9999/client/1/foo HTTP/1.1");
+            return Futures.immediateFuture(HttpResponse.builder().build());
+         }
+
+      });
 
       try {
          child.getInstance(AsyncCallee.class);
@@ -242,42 +239,21 @@
 
       }
 
-      AsyncCaller caller = child.getInstance(AsyncCaller.class);
-      expect(mock.submit(requestLineEquals("GET http://localhost:9999/client/1/foo HTTP/1.1"), eq(function)))
-            .andReturn(createNiceMock(ListenableFuture.class)).atLeastOnce();
-      replay(mock);
-
-      caller.getCallee().onePath("foo");
-
-      verify(mock);
+      child.getInstance(AsyncCaller.class).getCallee().onePath("foo").get();
 
    }
 
-   public static HttpCommand requestLineEquals(final String requestLine) {
-      reportMatcher(new IArgumentMatcher() {
+   public void testDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, ExecutionException {
+      Injector child = injectorForCaller(new HttpCommandExecutorService() {
 
          @Override
-         public void appendTo(StringBuffer buffer) {
-            buffer.append("requestLineEquals(");
-            buffer.append(requestLine);
-            buffer.append(")");
-         }
-
-         @Override
-         public boolean matches(Object arg) {
-            return ((HttpCommand) arg).getCurrentRequest().getRequestLine().equals(requestLine);
+         public Future<HttpResponse> submit(HttpCommand command) {
+            assertEquals(command.getCurrentRequest().getRequestLine(),
+                     "GET http://localhost:1111/client/1/foo HTTP/1.1");
+            return Futures.immediateFuture(HttpResponse.builder().build());
          }
 
       });
-      return null;
-   }
-
-   public void testDelegateWithOverridingEndpoint() throws SecurityException, NoSuchMethodException,
-         InterruptedException, ExecutionException {
-      Injector child = injectorForClient();
-      TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class);
-
-      ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class);
 
       try {
          child.getInstance(Callee.class);
@@ -286,23 +262,45 @@
 
       }
 
-      Caller caller = child.getInstance(Caller.class);
-      expect(mock.submit(requestLineEquals("GET http://localhost:1111/client/1/foo HTTP/1.1"), eq(function)))
-            .andReturn(Futures.<Void> immediateFuture(null)).atLeastOnce();
-      replay(mock);
+      child.getInstance(Caller.class).getCallee().onePath("foo");
 
-      caller.getCallee().onePath("foo");
+   }
+   
 
-      verify(mock);
+   public void testAsyncDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, ExecutionException {
+      Injector child = injectorForCaller(new HttpCommandExecutorService() {
+
+         @Override
+         public Future<HttpResponse> submit(HttpCommand command) {
+            assertEquals(command.getCurrentRequest().getRequestLine(),
+                     "GET http://howdyboys/client/1/foo HTTP/1.1");
+            return Futures.immediateFuture(HttpResponse.builder().build());
+         }
+
+      });
+
+      try {
+         child.getInstance(AsyncCallee.class);
+         assert false : "Callee shouldn't be bound yet";
+      } catch (ConfigurationException e) {
+
+      }
+
+      child.getInstance(AsyncCaller.class).getCallee(URI.create("http://howdyboys")).onePath("foo").get();
 
    }
 
-   public void testDelegateWithOverridingEndpointOnMethod() throws SecurityException, NoSuchMethodException,
-         InterruptedException, ExecutionException {
-      Injector child = injectorForClient();
-      TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class);
+   public void testDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, ExecutionException {
+      Injector child = injectorForCaller(new HttpCommandExecutorService() {
 
-      ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class);
+         @Override
+         public Future<HttpResponse> submit(HttpCommand command) {
+            assertEquals(command.getCurrentRequest().getRequestLine(),
+                     "GET http://howdyboys/client/1/foo HTTP/1.1");
+            return Futures.immediateFuture(HttpResponse.builder().build());
+         }
+
+      });
 
       try {
          child.getInstance(Callee.class);
@@ -311,22 +309,15 @@
 
       }
 
-      Caller caller = child.getInstance(Caller.class);
-      expect(mock.submit(requestLineEquals("GET http://howdyboys/client/1/foo HTTP/1.1"), eq(function))).andReturn(
-            Futures.<Void> immediateFuture(null)).atLeastOnce();
-      replay(mock);
-
-      caller.getCallee(URI.create("http://howdyboys")).onePath("foo");
-
-      verify(mock);
+      child.getInstance(Caller.class).getCallee(URI.create("http://howdyboys")).onePath("foo");
 
    }
+   
+   private Injector injectorForCaller(HttpCommandExecutorService service) {
 
-   private Injector injectorForClient() {
-
-      RestContextSpec<Caller, AsyncCaller> contextSpec = contextSpec("test", "http://localhost:9999", "1", "",
-            "userfoo", null, Caller.class, AsyncCaller.class,
-            ImmutableSet.<Module> of(new MockModule(), new NullLoggingModule(), new CallerCalleeModule()));
+      RestContextSpec<Caller, AsyncCaller> contextSpec = contextSpec("test", "http://localhost:9999", "1", "", "",
+               "userfoo", null, Caller.class, AsyncCaller.class, ImmutableSet.<Module> of(new MockModule(service),
+                        new NullLoggingModule(), new CallerModule()));
 
       return createContextBuilder(contextSpec).buildInjector();
 
@@ -2435,8 +2426,8 @@
 
    @BeforeClass
    void setupFactory() {
-      RestContextSpec<String, Integer> contextSpec = contextSpec("test", "http://localhost:9999", "1", "", "userfoo",
-            null, String.class, Integer.class,
+      RestContextSpec<Callee, AsyncCallee> contextSpec = contextSpec("test", "http://localhost:9999", "1", "", "", "userfoo",
+            null, Callee.class, AsyncCallee.class,
             ImmutableSet.<Module> of(new MockModule(), new NullLoggingModule(), new AbstractModule() {
 
                @Override
diff --git a/core/src/test/java/org/jclouds/util/Suppliers2Test.java b/core/src/test/java/org/jclouds/util/Suppliers2Test.java
new file mode 100644
index 0000000..9319451
--- /dev/null
+++ b/core/src/test/java/org/jclouds/util/Suppliers2Test.java
@@ -0,0 +1,239 @@
+/**
+ * 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.util;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+
+public class Suppliers2Test {
+
+   @Test
+   public void testMemoizeKeepsValueForFullDurationWhenDelegateCallIsSlow() {
+      final long SLEEP_TIME = 250;
+      final long EXPIRATION_TIME = 200;
+      
+      Supplier<Integer> slowSupplier = new CountingSupplier() {
+         private static final long serialVersionUID = 1L;
+         
+         @Override public Integer get() {
+           try {
+              Thread.sleep(SLEEP_TIME);
+           } catch (InterruptedException e) {
+              Thread.currentThread().interrupt();
+           }
+           return super.get();
+         }
+      };
+      
+      Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval(
+               slowSupplier, EXPIRATION_TIME, TimeUnit.MILLISECONDS);
+      
+      assertEquals(memoizedSupplier.get(), (Integer)10);
+      assertEquals(memoizedSupplier.get(), (Integer)10);
+   }
+   
+   // =================================
+   // 
+   // TODO Everything below this point is taken from SuppliersTest, to test our version of the Suppliers2.memoizeWithExpiration
+   // It should be deleted when we can switch back to using the google Supplier.memoizeWithExpiration. 
+   
+    private static class CountingSupplier implements Supplier<Integer>, Serializable {
+      private static final long serialVersionUID = 0L;
+      transient int calls = 0;
+      @Override
+      public Integer get() {
+        calls++;
+        return calls * 10;
+      }
+    }
+
+    @Test
+    public void testMemoizeWithExpiration() throws InterruptedException {
+      CountingSupplier countingSupplier = new CountingSupplier();
+
+      Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval(
+          countingSupplier, 75, TimeUnit.MILLISECONDS);
+
+      checkExpiration(countingSupplier, memoizedSupplier);
+    }
+
+    @Test
+    public void testMemoizeWithExpirationSerialized()
+        throws InterruptedException {
+      CountingSupplier countingSupplier = new CountingSupplier();
+
+      Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval(
+          countingSupplier, 75, TimeUnit.MILLISECONDS);
+      // Calls to the original memoized supplier shouldn't affect its copy.
+      memoizedSupplier.get();
+
+      Supplier<Integer> copy = reserialize(memoizedSupplier);
+      memoizedSupplier.get();
+
+      CountingSupplier countingCopy = (CountingSupplier)
+          ((Suppliers2.ExpiringMemoizingSupplier<Integer>) copy).delegate;
+      checkExpiration(countingCopy, copy);
+    }
+
+    private void checkExpiration(
+        CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)
+        throws InterruptedException {
+      // the underlying supplier hasn't executed yet
+      assertEquals(0, countingSupplier.calls);
+
+      assertEquals(10, (int) memoizedSupplier.get());
+      // now it has
+      assertEquals(1, countingSupplier.calls);
+
+      assertEquals(10, (int) memoizedSupplier.get());
+      // it still should only have executed once due to memoization
+      assertEquals(1, countingSupplier.calls);
+
+      Thread.sleep(150);
+
+      assertEquals(20, (int) memoizedSupplier.get());
+      // old value expired
+      assertEquals(2, countingSupplier.calls);
+
+      assertEquals(20, (int) memoizedSupplier.get());
+      // it still should only have executed twice due to memoization
+      assertEquals(2, countingSupplier.calls);
+    }
+
+    @Test
+    public void testExpiringMemoizedSupplierThreadSafe() throws Throwable {
+      Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
+          new Function<Supplier<Boolean>, Supplier<Boolean>>() {
+        @Override public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
+          return Suppliers2.memoizeWithExpirationOnAbsoluteInterval(
+              supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
+        }
+      };
+      testSupplierThreadSafe(memoizer);
+    }
+
+    public void testSupplierThreadSafe(
+        Function<Supplier<Boolean>, Supplier<Boolean>> memoizer)
+        throws Throwable {
+      final AtomicInteger count = new AtomicInteger(0);
+      final AtomicReference<Throwable> thrown =
+          new AtomicReference<Throwable>(null);
+      final int numThreads = 3;
+      final Thread[] threads = new Thread[numThreads];
+      final long timeout = TimeUnit.SECONDS.toNanos(60);
+
+      final Supplier<Boolean> supplier = new Supplier<Boolean>() {
+        boolean isWaiting(Thread thread) {
+          switch (thread.getState()) {
+            case BLOCKED:
+            case WAITING:
+            case TIMED_WAITING:
+            return true;
+            default:
+            return false;
+          }
+        }
+
+        int waitingThreads() {
+          int waitingThreads = 0;
+          for (Thread thread : threads) {
+            if (isWaiting(thread)) {
+              waitingThreads++;
+            }
+          }
+          return waitingThreads;
+        }
+
+        @Override
+        public Boolean get() {
+          // Check that this method is called exactly once, by the first
+          // thread to synchronize.
+          long t0 = System.nanoTime();
+          while (waitingThreads() != numThreads - 1) {
+            if (System.nanoTime() - t0 > timeout) {
+              thrown.set(new TimeoutException(
+                  "timed out waiting for other threads to block" +
+                  " synchronizing on supplier"));
+              break;
+            }
+            Thread.yield();
+          }
+          count.getAndIncrement();
+          return Boolean.TRUE;
+        }
+      };
+
+      final Supplier<Boolean> memoizedSupplier = memoizer.apply(supplier);
+
+      for (int i = 0; i < numThreads; i++) {
+        threads[i] = new Thread() {
+          @Override public void run() {
+            assertSame(Boolean.TRUE, memoizedSupplier.get());
+          }
+        };
+      }
+      for (Thread t : threads) {
+        t.start();
+      }
+      for (Thread t : threads) {
+        t.join();
+      }
+
+      if (thrown.get() != null) {
+        throw thrown.get();
+      }
+      assertEquals(1, count.get());
+    }
+    
+    // Taken from com.google.common.testing.SerializableTester
+    @SuppressWarnings("unchecked")
+    private static <T> T reserialize(T object) {
+      checkNotNull(object);
+      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+      try {
+        ObjectOutputStream out = new ObjectOutputStream(bytes);
+        out.writeObject(object);
+        ObjectInputStream in = new ObjectInputStream(
+            new ByteArrayInputStream(bytes.toByteArray()));
+        return (T) in.readObject();
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      } catch (ClassNotFoundException e) {
+        throw new RuntimeException(e);
+      }
+    }
+}
diff --git a/core/src/test/java/org/jclouds/util/Throwables2Test.java b/core/src/test/java/org/jclouds/util/Throwables2Test.java
index f449fe7..ecc89b9 100644
--- a/core/src/test/java/org/jclouds/util/Throwables2Test.java
+++ b/core/src/test/java/org/jclouds/util/Throwables2Test.java
@@ -116,6 +116,12 @@
       returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(new Class[] {}, new RuntimeException(e));
    }
 
+   @Test(expectedExceptions = AssertionError.class)
+   public void testPropagateStandardExceptionAssertionError() throws Exception {
+      AssertionError e = new AssertionError();
+      returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(new Class[] {}, new RuntimeException(e));
+   }
+
    @Test(expectedExceptions = AuthorizationException.class)
    public void testPropagateStandardExceptionAuthorizationException() throws Exception {
       Exception e = new AuthorizationException();
diff --git a/core/src/test/resources/META-INF/maven/org.jclouds/jclouds-core/pom.properties b/core/src/test/resources/META-INF/maven/org.jclouds/jclouds-core/pom.properties
new file mode 100644
index 0000000..6cc82e2
--- /dev/null
+++ b/core/src/test/resources/META-INF/maven/org.jclouds/jclouds-core/pom.properties
@@ -0,0 +1,4 @@
+# Dummy pom.properties file for testing
+version=0.0.0-SNAPSHOT
+groupId=org.jclouds
+artifactId=jclouds-core
diff --git a/demos/tweetstore/gae-tweetstore-spring/pom.xml b/demos/tweetstore/gae-tweetstore-spring/pom.xml
index 8ef5caa..7652afb 100644
--- a/demos/tweetstore/gae-tweetstore-spring/pom.xml
+++ b/demos/tweetstore/gae-tweetstore-spring/pom.xml
@@ -136,7 +136,7 @@
                     <plugin>
                         <groupId>net.kindleit</groupId>
                         <artifactId>maven-gae-plugin</artifactId>
-                        <version>0.9.1</version>
+                        <version>0.9.2</version>
                         <configuration>
                             <serverId>google-appengine</serverId>
                             <sdkDir>${appengine.sdk.root}</sdkDir>
diff --git a/demos/tweetstore/gae-tweetstore/pom.xml b/demos/tweetstore/gae-tweetstore/pom.xml
index d1d5393..37a0d45 100644
--- a/demos/tweetstore/gae-tweetstore/pom.xml
+++ b/demos/tweetstore/gae-tweetstore/pom.xml
@@ -125,7 +125,7 @@
                     <plugin>
                         <groupId>net.kindleit</groupId>
                         <artifactId>maven-gae-plugin</artifactId>
-                        <version>0.8.4</version>
+                        <version>0.9.2</version>
                         <configuration>
                             <serverId>google-appengine</serverId>
                             <sdkDir>${appengine.sdk.root}</sdkDir>
diff --git a/drivers/jsch/src/main/java/org/jclouds/ssh/jsch/JschSshClient.java b/drivers/jsch/src/main/java/org/jclouds/ssh/jsch/JschSshClient.java
index 6e107bf..a4081c4 100644
--- a/drivers/jsch/src/main/java/org/jclouds/ssh/jsch/JschSshClient.java
+++ b/drivers/jsch/src/main/java/org/jclouds/ssh/jsch/JschSshClient.java
@@ -35,7 +35,6 @@
 import java.net.ConnectException;
 import java.util.Arrays;
 
-import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import javax.annotation.Resource;
 import javax.inject.Named;
@@ -224,7 +223,6 @@
       return null;
    }
 
-   @PostConstruct
    public void connect() {
       acquire(sessionConnection);
    }
diff --git a/drivers/sshj/pom.xml b/drivers/sshj/pom.xml
index d47ecfc..1f72cb0 100644
--- a/drivers/sshj/pom.xml
+++ b/drivers/sshj/pom.xml
@@ -89,7 +89,7 @@
         <dependency>
             <groupId>net.schmizz</groupId>
             <artifactId>sshj</artifactId>
-            <version>0.6.1</version>
+            <version>0.7.0</version>
         </dependency>
         <dependency>
             <groupId>commons-io</groupId>
diff --git a/drivers/sshj/src/main/java/org/jclouds/sshj/SshjSshClient.java b/drivers/sshj/src/main/java/org/jclouds/sshj/SshjSshClient.java
index 5bd3758..9427141 100644
--- a/drivers/sshj/src/main/java/org/jclouds/sshj/SshjSshClient.java
+++ b/drivers/sshj/src/main/java/org/jclouds/sshj/SshjSshClient.java
@@ -36,7 +36,6 @@
 import java.net.SocketTimeoutException;
 import java.util.concurrent.TimeUnit;
 
-import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import javax.annotation.Resource;
 import javax.inject.Named;
@@ -248,7 +247,6 @@
       return null;
    }
 
-   @PostConstruct
    public void connect() {
       try {
          ssh = acquire(sshConnection);
diff --git a/loadbalancer/src/main/java/org/jclouds/loadbalancer/config/BaseLoadBalancerServiceContextModule.java b/loadbalancer/src/main/java/org/jclouds/loadbalancer/config/BaseLoadBalancerServiceContextModule.java
index 1f61d36..75a5a68 100644
--- a/loadbalancer/src/main/java/org/jclouds/loadbalancer/config/BaseLoadBalancerServiceContextModule.java
+++ b/loadbalancer/src/main/java/org/jclouds/loadbalancer/config/BaseLoadBalancerServiceContextModule.java
@@ -20,7 +20,6 @@
 
 import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
 
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -32,10 +31,7 @@
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 
-import com.google.common.base.Function;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.collect.Maps;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 
@@ -54,27 +50,6 @@
 
    @Provides
    @Singleton
-   protected Supplier<Map<String, ? extends Location>> provideLocationMap(
-         @Memoized Supplier<Set<? extends Location>> locations) {
-      return Suppliers.compose(new Function<Set<? extends Location>, Map<String, ? extends Location>>() {
-
-         @Override
-         public Map<String, ? extends Location> apply(Set<? extends Location> from) {
-            return Maps.uniqueIndex(from, new Function<Location, String>() {
-
-               @Override
-               public String apply(Location from) {
-                  return from.getId();
-               }
-
-            });
-         }
-
-      }, locations);
-   }
-
-   @Provides
-   @Singleton
    @Memoized
    protected Supplier<Set<? extends Location>> supplyLocationCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
          final Supplier<Set<? extends Location>> locationSupplier) {
diff --git a/loadbalancer/src/test/java/org/jclouds/loadbalancer/BaseLoadBalancerServiceLiveTest.java b/loadbalancer/src/test/java/org/jclouds/loadbalancer/BaseLoadBalancerServiceLiveTest.java
index c797dfa..9fa21cf 100644
--- a/loadbalancer/src/test/java/org/jclouds/loadbalancer/BaseLoadBalancerServiceLiveTest.java
+++ b/loadbalancer/src/test/java/org/jclouds/loadbalancer/BaseLoadBalancerServiceLiveTest.java
@@ -55,7 +55,7 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
+@Test(groups = "live", singleThreaded = true)
 public abstract class BaseLoadBalancerServiceLiveTest extends BaseVersionedServiceLiveTest {
 
    protected SshClient.Factory sshFactory;
@@ -74,6 +74,7 @@
    protected String computeCredential;
    protected String computeEndpoint;
    protected String computeApiversion;
+   protected String computeBuildversion;
    protected ComputeServiceContext computeContext;
 
    @Override
@@ -85,7 +86,8 @@
             + ".compute.identity");
       computeCredential = System.getProperty("test." + provider + ".compute.credential");
       computeEndpoint = System.getProperty("test." + provider + ".compute.endpoint");
-      computeApiversion = System.getProperty("test." + provider + ".compute.apiversion");
+      computeApiversion = System.getProperty("test." + provider + ".compute.api-version");
+      computeBuildversion = System.getProperty("test." + provider + ".compute.build-version");
    }
 
    protected Properties setupComputeProperties() {
@@ -98,7 +100,9 @@
       if (computeEndpoint != null)
          overrides.setProperty(computeProvider + ".endpoint", computeEndpoint);
       if (computeApiversion != null)
-         overrides.setProperty(computeProvider + ".apiversion", computeApiversion);
+         overrides.setProperty(computeProvider + ".api-version", computeApiversion);
+      if (computeBuildversion != null)
+         overrides.setProperty(computeProvider + ".build-version", computeBuildversion);
       return overrides;
    }
 
diff --git a/project/pom.xml b/project/pom.xml
index dbbc587..bf7f149 100644
--- a/project/pom.xml
+++ b/project/pom.xml
@@ -206,8 +206,13 @@
         <dependencies>
             <dependency>
                 <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-server</artifactId>
+                <version>8.0.4.v20111024</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
                 <artifactId>jetty-security</artifactId>
-                <version>7.4.0.RC0</version>
+                <version>8.0.4.v20111024</version>
             </dependency>
             <dependency>
                 <groupId>com.jcraft</groupId>
@@ -276,7 +281,7 @@
             <plugin>
                 <groupId>com.theoryinpractise</groupId>
                 <artifactId>clojure-maven-plugin</artifactId>
-                <version>1.3.3</version>
+                <version>1.3.8</version>
                 <configuration>
                     <sourceDirectories>
                         <sourceDirectory>src/main/clojure</sourceDirectory>
diff --git a/providers/aws-cloudwatch/pom.xml b/providers/aws-cloudwatch/pom.xml
index 013a1b7..d77927a 100644
--- a/providers/aws-cloudwatch/pom.xml
+++ b/providers/aws-cloudwatch/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.aws-cloudwatch.endpoint>https://monitoring.us-east-1.amazonaws.com</test.aws-cloudwatch.endpoint>
-        <test.aws-cloudwatch.apiversion>2009-05-15</test.aws-cloudwatch.apiversion>
+        <test.aws-cloudwatch.api-version>2009-05-15</test.aws-cloudwatch.api-version>
+        <test.aws-cloudwatch.build-version></test.aws-cloudwatch.build-version>
         <test.aws-cloudwatch.identity>${test.aws.identity}</test.aws-cloudwatch.identity>
         <test.aws-cloudwatch.credential>${test.aws.credential}</test.aws-cloudwatch.credential>
     </properties>
@@ -87,12 +88,13 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.aws-cloudwatch.endpoint>${test.aws-cloudwatch.endpoint}</test.aws-cloudwatch.endpoint>
-                                        <test.aws-cloudwatch.apiversion>${test.aws-cloudwatch.apiversion}</test.aws-cloudwatch.apiversion>
+                                        <test.aws-cloudwatch.api-version>${test.aws-cloudwatch.api-version}</test.aws-cloudwatch.api-version>
+                                        <test.aws-cloudwatch.build-version>${test.aws-cloudwatch.build-version}</test.aws-cloudwatch.build-version>
                                         <test.aws-cloudwatch.identity>${test.aws-cloudwatch.identity}</test.aws-cloudwatch.identity>
                                         <test.aws-cloudwatch.credential>${test.aws-cloudwatch.credential}</test.aws-cloudwatch.credential>
                                         <test.aws-cloudwatch.compute.provider>${test.aws-cloudwatch.compute.provider}</test.aws-cloudwatch.compute.provider>
                                         <test.aws-cloudwatch.compute.endpoint>${test.aws-cloudwatch.compute.endpoint}</test.aws-cloudwatch.compute.endpoint>
-                                        <test.aws-cloudwatch.compute.apiversion>${test.aws-cloudwatch.compute.apiversion}</test.aws-cloudwatch.compute.apiversion>
+                                        <test.aws-cloudwatch.compute.api-version>${test.aws-cloudwatch.compute.api-version}</test.aws-cloudwatch.compute.api-version>
                                         <test.aws-cloudwatch.compute.identity>${test.aws-cloudwatch.compute.identity}</test.aws-cloudwatch.compute.identity>
                                         <test.aws-cloudwatch.compute.credential>${test.aws-cloudwatch.compute.credential}</test.aws-cloudwatch.compute.credential>
                                     </systemPropertyVariables>
diff --git a/providers/aws-ec2/pom.xml b/providers/aws-ec2/pom.xml
index e3477d5..9328e8d 100644
--- a/providers/aws-ec2/pom.xml
+++ b/providers/aws-ec2/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.aws-ec2.endpoint>https://ec2.us-east-1.amazonaws.com</test.aws-ec2.endpoint>
-    <test.aws-ec2.apiversion>2011-05-15</test.aws-ec2.apiversion>
+    <test.aws-ec2.api-version>2011-05-15</test.aws-ec2.api-version>
+    <test.aws-ec2.build-version></test.aws-ec2.build-version>
     <test.aws-ec2.identity>${test.aws.identity}</test.aws-ec2.identity>
     <test.aws-ec2.credential>${test.aws.credential}</test.aws-ec2.credential>
     <test.aws-ec2.image-id />
@@ -113,7 +114,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.aws-ec2.endpoint>${test.aws-ec2.endpoint}</test.aws-ec2.endpoint>
-                    <test.aws-ec2.apiversion>${test.aws-ec2.apiversion}</test.aws-ec2.apiversion>
+                    <test.aws-ec2.api-version>${test.aws-ec2.api-version}</test.aws-ec2.api-version>
+                    <test.aws-ec2.build-version>${test.aws-ec2.build-version}</test.aws-ec2.build-version>
                     <test.aws-ec2.identity>${test.aws-ec2.identity}</test.aws-ec2.identity>
                     <test.aws-ec2.credential>${test.aws-ec2.credential}</test.aws-ec2.credential>
                     <test.aws-ec2.image-id>${test.aws-ec2.image-id}</test.aws-ec2.image-id>
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/suppliers/AWSEC2ImageSupplier.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/suppliers/AWSEC2ImageSupplier.java
index 2f03987..b1dd1d1 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/suppliers/AWSEC2ImageSupplier.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/suppliers/AWSEC2ImageSupplier.java
@@ -34,6 +34,7 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import com.google.common.collect.ForwardingSet;
 import org.jclouds.Constants;
 import org.jclouds.aws.ec2.compute.config.ClusterCompute;
 import org.jclouds.compute.domain.Image;
@@ -127,7 +128,11 @@
       logger.debug("<< images(%d)", imageMap.size());
       
       // TODO Used to be mutable; was this assumed anywhere?
-      return ImmutableSet.copyOf(imageMap.values());
+      return new ForwardingSet<Image>() {
+         protected Set<Image> delegate() {
+            return ImmutableSet.copyOf(cache.get().asMap().values());
+         }
+      };
    }
    
    private Future<Iterable<Image>> images(Iterable<String> regions, String query, String tag) {
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java
index c62cff6..8d41a26 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.aws.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.aws.ec2.options.AWSDescribeImagesOptions.Builder.filters;
 import static org.jclouds.aws.ec2.options.AWSDescribeImagesOptions.Builder.imageIds;
 import static org.jclouds.ec2.options.RegisterImageBackedByEbsOptions.Builder.addNewBlockDevice;
@@ -30,22 +29,32 @@
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.predicates.ImagePredicates;
 import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.EC2Client;
+import org.jclouds.ec2.domain.BlockDevice;
 import org.jclouds.ec2.domain.Image;
+import org.jclouds.ec2.domain.Reservation;
 import org.jclouds.ec2.domain.RootDeviceType;
+import org.jclouds.ec2.domain.RunningInstance;
+import org.jclouds.ec2.domain.Snapshot;
 import org.jclouds.ec2.domain.Image.ImageType;
 import org.jclouds.ec2.services.AMIClient;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.rest.RestContext;
 import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
@@ -59,50 +68,29 @@
  * @author Adrian Cole
  */
 @Test(groups = "live", singleThreaded = true)
-public class AMIClientLiveTest {
-
+public class AMIClientLiveTest extends BaseVersionedServiceLiveTest {
+   public AMIClientLiveTest() {
+      provider = "aws-ec2";
+      // TODO: parameterize this.
+      imageId = "ami-cdf819a4";
+   }
+   
    private AMIClient client;
-   private String imageId = "ami-cdf819a4";
    private static final String DEFAULT_MANIFEST = "adrianimages/image.manifest.xml";
    private static final String DEFAULT_SNAPSHOT = "TODO";
    private RestContext<EC2Client, EC2AsyncClient> context;
 
    private Set<String> imagesToDeregister = Sets.newHashSet();
+   private Set<String> snapshotsToDelete = Sets.newHashSet();
 
-   protected String provider = "aws-ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
+   private ComputeServiceContext jcloudsContext;
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
       Properties overrides = setupProperties();
-      context = new ComputeServiceContextFactory().createContext(provider,
-               ImmutableSet.<Module> of(new Log4JLoggingModule()), overrides).getProviderSpecificContext();
+      jcloudsContext = new ComputeServiceContextFactory().createContext(provider,
+         ImmutableSet.<Module>of(new Log4JLoggingModule()), overrides);
+      context = jcloudsContext.getProviderSpecificContext();
       client = context.getApi().getAMIServices();
    }
 
@@ -175,6 +163,50 @@
       assertEquals(imageRegisteredFromManifestWithOptions.getDescription(), "adrian");
    }
 
+   @Test
+   public void testNewlyRegisteredImageCanBeListed() throws Exception {
+      ComputeService computeService = jcloudsContext.getComputeService();
+      Snapshot snapshot = createSnapshot(computeService);
+
+      // List of images before...
+      int sizeBefore = computeService.listImages().size();
+
+      // Register a new image...
+      final String imageRegisteredId = client.registerUnixImageBackedByEbsInRegion(null, "jcloudstest1", snapshot.getId());
+      imagesToDeregister.add(imageRegisteredId);
+      final Image imageRegistered = Iterables.getOnlyElement(client.describeImagesInRegion(null, imageIds(imageRegisteredId)));
+
+      // This is the suggested method to ensure the new image ID is inserted into the cache
+      // (suggested by adriancole_ on #jclouds)
+      computeService.templateBuilder().imageId(imageRegistered.getRegion() + "/" + imageRegisteredId).build();
+
+      // List of images after - should be one larger than before
+      Set<? extends org.jclouds.compute.domain.Image> after = computeService.listImages();
+      assertEquals(after.size(), sizeBefore + 1);
+
+      // Detailed check: filter for the AMI ID
+      Iterable<? extends org.jclouds.compute.domain.Image> filtered = Iterables.filter(after,
+         ImagePredicates.idEquals(imageRegistered.getRegion() + "/" + imageRegisteredId));
+      assertEquals(Iterables.size(filtered), 1);
+   }
+
+   // Fires up an instance, finds its root volume ID, takes a snapshot, then terminates the instance.
+   private Snapshot createSnapshot(ComputeService computeService) throws RunNodesException {
+      Template options = computeService.templateBuilder().smallest().build();
+      Set<? extends NodeMetadata> nodes = computeService.createNodesInGroup("jcloudstest", 1, options);
+      try {
+         String instanceId = Iterables.getOnlyElement(nodes).getProviderId();
+         Reservation<? extends RunningInstance> reservation = Iterables.getOnlyElement(context.getApi().getInstanceServices().describeInstancesInRegion(null, instanceId));
+         RunningInstance instance = Iterables.getOnlyElement(reservation);
+         BlockDevice device = instance.getEbsBlockDevices().get("/dev/sda1");
+         Snapshot snapshot = context.getApi().getElasticBlockStoreServices().createSnapshotInRegion(null, device.getVolumeId());
+         snapshotsToDelete.add(snapshot.getId());
+         return snapshot;
+      } finally {
+         computeService.destroyNodesMatching(Predicates.in(nodes));
+      }
+   }
+
    @Test(enabled = false)
    // awaiting EBS functionality to be added to jclouds
    public void testRegisterImageBackedByEBS() {
@@ -245,8 +277,10 @@
    }
 
    @AfterTest
-   public void deregisterImages() {
+   public void cleanUp() {
       for (String imageId : imagesToDeregister)
          client.deregisterImageInRegion(null, imageId);
+      for (String snapshotId : snapshotsToDelete)
+         context.getApi().getElasticBlockStoreServices().deleteSnapshotInRegion(null, snapshotId);
    }
 }
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIClientLiveTest.java
index a46e6b9..ebeeb4c 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.aws.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.aws.ec2.options.AWSDescribeImagesOptions.Builder.imageIds;
 import static org.jclouds.ec2.options.RegisterImageBackedByEbsOptions.Builder.addNewBlockDevice;
 import static org.jclouds.ec2.options.RegisterImageOptions.Builder.withDescription;
@@ -29,9 +28,9 @@
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.AWSEC2Client;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.domain.Image;
 import org.jclouds.ec2.domain.RootDeviceType;
@@ -40,7 +39,6 @@
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.RestContext;
 import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
@@ -55,45 +53,21 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class AWSAMIClientLiveTest {
-
+@Test(groups = "live", singleThreaded = true, testName = "AWSAMIClientLiveTest")
+public class AWSAMIClientLiveTest extends BaseVersionedServiceLiveTest {
+   public AWSAMIClientLiveTest() {
+      provider = "aws-ec2";
+      // TODO: parameterize this.
+      imageId = "ami-7ea24a17";
+   }
+   
    private AWSAMIClient client;
-   private String imageId = "ami-7ea24a17";
    private static final String DEFAULT_MANIFEST = "adrianimages/image.manifest.xml";
    private static final String DEFAULT_SNAPSHOT = "TODO";
    private RestContext<AWSEC2Client, AWSEC2AsyncClient> context;
 
    private Set<String> imagesToDeregister = Sets.newHashSet();
 
-   protected String provider = "aws-ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSInstanceClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSInstanceClientLiveTest.java
index 6f59c9e..5554a5b 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSInstanceClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSInstanceClientLiveTest.java
@@ -18,15 +18,14 @@
  */
 package org.jclouds.aws.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertNotNull;
 
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.AWSEC2Client;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.domain.Reservation;
 import org.jclouds.ec2.domain.RunningInstance;
@@ -44,39 +43,17 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class AWSInstanceClientLiveTest {
+@Test(groups = "live", singleThreaded = true)
+public class AWSInstanceClientLiveTest extends BaseVersionedServiceLiveTest {
+   public AWSInstanceClientLiveTest() {
+      provider = "aws-ec2";
+   }
+   
    public static final String PREFIX = System.getProperty("user.name") + "-ec2";
 
    private AWSInstanceClient client;
    private RestContext<AWSEC2Client, AWSEC2AsyncClient> context;
-   protected String provider = "aws-ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-            + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
+   
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java
index fb6bb41..efe34ee 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.aws.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.get;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.common.collect.Sets.newTreeSet;
@@ -36,12 +35,12 @@
 import java.util.Set;
 import java.util.SortedSet;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
 import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.AWSEC2Client;
 import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
 import org.jclouds.aws.ec2.domain.AWSRunningInstance;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.compute.ComputeTestUtils;
@@ -66,39 +65,16 @@
  * @author Adrian Cole
  */
 @Test(groups = "live", singleThreaded = true)
-public class AWSKeyPairClientLiveTest {
+public class AWSKeyPairClientLiveTest extends BaseVersionedServiceLiveTest {
+   public AWSKeyPairClientLiveTest() {
+      provider = "aws-ec2";
+   }
 
    private AWSKeyPairClient client;
    private RestContext<AWSEC2Client, AWSEC2AsyncClient> context;
 
-   protected String provider = "aws-ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    private ComputeServiceContext computeContext;
 
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint", null);
-      apiversion = System.getProperty("test." + provider + ".apiversion", null);
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringClientLiveTest.java
index b729c09..4cd81c9 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringClientLiveTest.java
@@ -18,16 +18,15 @@
  */
 package org.jclouds.aws.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 
 import java.util.Map;
 import java.util.Properties;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.AWSEC2Client;
 import org.jclouds.aws.ec2.domain.MonitoringState;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.rest.RestContext;
@@ -42,37 +41,16 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class MonitoringClientLiveTest {
+@Test(groups = "live", singleThreaded = true)
+public class MonitoringClientLiveTest extends BaseVersionedServiceLiveTest {
+   public MonitoringClientLiveTest() {
+      provider = "aws-ec2";
+   }
 
    private MonitoringClient client;
    private static final String DEFAULT_INSTANCE = "i-TODO";
    private RestContext<AWSEC2Client, AWSEC2AsyncClient> context;
-   protected String provider = "aws-ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
 
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-               + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java
index e4ff0a2..96f4002 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.aws.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Sets.newTreeSet;
@@ -33,13 +32,13 @@
 import java.util.SortedSet;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
 import org.jclouds.aws.ec2.AWSEC2Client;
 import org.jclouds.aws.ec2.domain.PlacementGroup;
 import org.jclouds.aws.ec2.domain.PlacementGroup.State;
 import org.jclouds.aws.ec2.predicates.PlacementGroupAvailable;
 import org.jclouds.aws.ec2.predicates.PlacementGroupDeleted;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.compute.RunNodesException;
@@ -54,7 +53,6 @@
 import org.jclouds.scriptbuilder.statements.login.AdminAccess;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
@@ -68,40 +66,16 @@
  * @author Adrian Cole
  */
 @Test(groups = "live", singleThreaded = true, testName = "PlacementGroupClientLiveTest")
-public class PlacementGroupClientLiveTest {
+public class PlacementGroupClientLiveTest extends BaseVersionedServiceLiveTest {
+   public PlacementGroupClientLiveTest() {
+      provider = "aws-ec2";
+   }
 
    private AWSEC2Client client;
    private ComputeServiceContext context;
    private RetryablePredicate<PlacementGroup> availableTester;
    private RetryablePredicate<PlacementGroup> deletedTester;
    private PlacementGroup group;
-   protected String provider = "aws-ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() throws FileNotFoundException, IOException {
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/SpotInstanceClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/SpotInstanceClientLiveTest.java
index fc06e17..7a4d638 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/SpotInstanceClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/SpotInstanceClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.aws.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Predicates.in;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static org.jclouds.aws.ec2.options.DescribeSpotPriceHistoryOptions.Builder.from;
@@ -34,7 +33,6 @@
 import java.util.SortedSet;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
 import org.jclouds.aws.ec2.AWSEC2Client;
 import org.jclouds.aws.ec2.domain.AWSRunningInstance;
@@ -42,6 +40,7 @@
 import org.jclouds.aws.ec2.domain.Spot;
 import org.jclouds.aws.ec2.domain.SpotInstanceRequest;
 import org.jclouds.aws.ec2.predicates.SpotInstanceRequestActive;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.ec2.domain.InstanceType;
@@ -49,7 +48,6 @@
 import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
@@ -63,43 +61,19 @@
  * @author Adrian Cole
  */
 @Test(groups = "live", singleThreaded = true)
-public class SpotInstanceClientLiveTest {
+public class SpotInstanceClientLiveTest  extends BaseVersionedServiceLiveTest {
+   public SpotInstanceClientLiveTest() {
+      provider = "aws-ec2";
+   }
 
    private static final int SPOT_DELAY_SECONDS = 600;
    private AWSEC2Client client;
    private ComputeServiceContext context;
    private RetryablePredicate<SpotInstanceRequest> activeTester;
    private Set<SpotInstanceRequest> requests;
-   protected String provider = "aws-ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    private AWSRunningInstance instance;
    private long start;
 
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() throws FileNotFoundException, IOException {
       setupCredentials();
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java
index 88239d4..901b52d 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.aws.ec2.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -26,13 +25,13 @@
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.AWSEC2Client;
 import org.jclouds.aws.ec2.domain.AWSRunningInstance;
 import org.jclouds.aws.ec2.domain.Tag;
 import org.jclouds.aws.ec2.util.TagFilters;
 import org.jclouds.aws.ec2.util.TagFilters.ResourceType;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
@@ -55,38 +54,17 @@
  * @author grkvlt@apache.org
  */
 @Test(groups = "live", singleThreaded = true)
-public class TagClientLiveTest {
+public class TagClientLiveTest extends BaseVersionedServiceLiveTest {
+   public TagClientLiveTest() {
+      provider = "aws-ec2";
+   }
 
    private TagClient client;
    private RestContext<AWSEC2Client, AWSEC2AsyncClient> context;
 
-   protected String provider = "aws-ec2";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    protected String testGroup;
    private ComputeServiceContext computeContext;
 
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint", null);
-      apiversion = System.getProperty("test." + provider + ".apiversion", null);
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClientAndSecurityGroup() {
diff --git a/providers/aws-s3/pom.xml b/providers/aws-s3/pom.xml
index a332487..2ffd814 100644
--- a/providers/aws-s3/pom.xml
+++ b/providers/aws-s3/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.aws.s3.blobstore.integration.AWSS3TestInitializer</test.initializer>
         <test.aws-s3.endpoint>https://s3.amazonaws.com</test.aws-s3.endpoint>
-        <test.aws-s3.apiversion>2006-03-01</test.aws-s3.apiversion>
+        <test.aws-s3.api-version>2006-03-01</test.aws-s3.api-version>
+        <test.aws-s3.build-version></test.aws-s3.build-version>
         <test.aws-s3.identity>${test.aws.identity}</test.aws-s3.identity>
         <test.aws-s3.credential>${test.aws.credential}</test.aws-s3.credential>
         <test.blobstore.container-count>25</test.blobstore.container-count>
@@ -123,7 +124,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.aws-s3.endpoint>${test.aws-s3.endpoint}</test.aws-s3.endpoint>
-                                        <test.aws-s3.apiversion>${test.aws-s3.apiversion}</test.aws-s3.apiversion>
+                                        <test.aws-s3.api-version>${test.aws-s3.api-version}</test.aws-s3.api-version>
+                                        <test.aws-s3.build-version>${test.aws-s3.build-version}</test.aws-s3.build-version>
                                         <test.aws-s3.identity>${test.aws-s3.identity}</test.aws-s3.identity>
                                         <test.aws-s3.credential>${test.aws-s3.credential}</test.aws-s3.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/binders/BindObjectMetadataToRequestTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/binders/BindObjectMetadataToRequestTest.java
index 118b42a..1c0c811 100644
--- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/binders/BindObjectMetadataToRequestTest.java
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/binders/BindObjectMetadataToRequestTest.java
@@ -28,10 +28,10 @@
 import org.jclouds.blobstore.binders.BindMapToHeadersWithPrefix;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
-import org.jclouds.s3.BaseS3AsyncClientTest;
 import org.jclouds.s3.S3AsyncClient;
 import org.jclouds.s3.domain.ObjectMetadata;
 import org.jclouds.s3.domain.ObjectMetadataBuilder;
+import org.jclouds.s3.internal.BaseS3AsyncClientTest;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMap;
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3TestInitializer.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3TestInitializer.java
index 37d13a8..5ba408b 100644
--- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3TestInitializer.java
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3TestInitializer.java
@@ -42,11 +42,11 @@
    }
 
    @Override
-   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiversion,
-            String app, String identity, String credential) throws IOException {
+   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiVersion,
+            String buildVersion, String app, String identity, String credential) throws IOException {
       return new BlobStoreContextFactory().createContext(provider, ImmutableSet.of(configurationModule,
-               new Log4JLoggingModule(), new EnterpriseConfigurationModule()), setupProperties(endpoint, apiversion,
-               identity, credential));
+               new Log4JLoggingModule(), new EnterpriseConfigurationModule()), setupProperties(endpoint, apiVersion,
+               buildVersion, identity, credential));
    }
 
 }
diff --git a/providers/azureblob/pom.xml b/providers/azureblob/pom.xml
index f7d398f..e573264 100644
--- a/providers/azureblob/pom.xml
+++ b/providers/azureblob/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.azureblob.blobstore.integration.AzureBlobTestInitializer</test.initializer>
         <test.azureblob.endpoint>https://{identity}.blob.core.windows.net</test.azureblob.endpoint>
-        <test.azureblob.apiversion>2009-09-19</test.azureblob.apiversion>
+        <test.azureblob.api-version>2009-09-19</test.azureblob.api-version>
+        <test.azureblob.build-version></test.azureblob.build-version>
         <test.azureblob.identity>${test.azure.identity}</test.azureblob.identity>
         <test.azureblob.credential>${test.azure.credential}</test.azureblob.credential>
     </properties>
@@ -94,7 +95,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.azureblob.endpoint>${test.azureblob.endpoint}</test.azureblob.endpoint>
-                                        <test.azureblob.apiversion>${test.azureblob.apiversion}</test.azureblob.apiversion>
+                                        <test.azureblob.api-version>${test.azureblob.api-version}</test.azureblob.api-version>
+                                        <test.azureblob.build-version>${test.azureblob.build-version}</test.azureblob.build-version>
                                         <test.azureblob.identity>${test.azureblob.identity}</test.azureblob.identity>
                                         <test.azureblob.credential>${test.azureblob.credential}</test.azureblob.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java
index 118739b..22ecdfc 100644
--- a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java
+++ b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java
@@ -18,10 +18,9 @@
  */
 package org.jclouds.azureblob;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.azure.storage.options.ListOptions.Builder.includeMetadata;
 import static org.jclouds.azureblob.options.CreateContainerOptions.Builder.withMetadata;
 import static org.jclouds.azureblob.options.CreateContainerOptions.Builder.withPublicAccess;
-import static org.jclouds.azure.storage.options.ListOptions.Builder.includeMetadata;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
@@ -32,16 +31,15 @@
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
 import org.jclouds.azure.storage.AzureStorageResponseException;
+import org.jclouds.azure.storage.domain.BoundedSet;
+import org.jclouds.azure.storage.options.ListOptions;
 import org.jclouds.azureblob.domain.AzureBlob;
 import org.jclouds.azureblob.domain.BlobProperties;
 import org.jclouds.azureblob.domain.ContainerProperties;
 import org.jclouds.azureblob.domain.ListBlobsResponse;
 import org.jclouds.azureblob.domain.PublicAccess;
 import org.jclouds.azureblob.options.ListBlobsOptions;
-import org.jclouds.azure.storage.domain.BoundedSet;
-import org.jclouds.azure.storage.options.ListOptions;
 import org.jclouds.blobstore.BlobStoreContext;
 import org.jclouds.blobstore.BlobStoreContextFactory;
 import org.jclouds.blobstore.ContainerNotFoundException;
@@ -50,6 +48,7 @@
 import org.jclouds.http.options.GetOptions;
 import org.jclouds.io.Payloads;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
+import org.jclouds.rest.BaseRestClientLiveTest;
 import org.jclouds.util.Strings2;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
@@ -65,40 +64,14 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class AzureBlobClientLiveTest {
-
+@Test(groups = "live", singleThreaded = true)
+public class AzureBlobClientLiveTest extends BaseRestClientLiveTest {
+   public AzureBlobClientLiveTest() {
+      provider = "azureblob";
+   }
    protected AzureBlobClient client;
 
-   private String containerPrefix = System.getProperty("user.name") + "-azureblob";
-
    private BlobStoreContext context;
-   protected String provider = "azureblob";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (credential != null)
-         overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
@@ -125,7 +98,7 @@
    public void testCreateContainer() throws Exception {
       boolean created = false;
       while (!created) {
-         privateContainer = containerPrefix + new SecureRandom().nextInt();
+         privateContainer = prefix + new SecureRandom().nextInt();
          try {
             created = client.createContainer(privateContainer, withMetadata(ImmutableMultimap.of("foo", "bar")));
          } catch (UndeclaredThrowableException e) {
@@ -149,7 +122,7 @@
    public void testCreatePublicContainer() throws Exception {
       boolean created = false;
       while (!created) {
-         publicContainer = containerPrefix + new SecureRandom().nextInt();
+         publicContainer = prefix + new SecureRandom().nextInt();
          try {
             created = client.createContainer(publicContainer, withPublicAccess(PublicAccess.BLOB));
          } catch (UndeclaredThrowableException e) {
diff --git a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobTestInitializer.java b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobTestInitializer.java
index 51b817c..5dfcd0a 100644
--- a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobTestInitializer.java
+++ b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobTestInitializer.java
@@ -39,10 +39,10 @@
    }
 
    @Override
-   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiversion,
-            String app, String identity, String credential) throws IOException {
+   protected BlobStoreContext createLiveContext(Module configurationModule, String endpoint, String apiVersion,
+            String buildVersion, String app, String identity, String credential) throws IOException {
       return new BlobStoreContextFactory().createContext(provider, ImmutableSet.of(configurationModule,
-               new Log4JLoggingModule()), setupProperties(endpoint, apiversion, identity, credential));
+               new Log4JLoggingModule()), setupProperties(endpoint, apiVersion, buildVersion, identity, credential));
    }
 
 }
diff --git a/providers/bluelock-vcloud-zone01/pom.xml b/providers/bluelock-vcloud-zone01/pom.xml
index 559ee9e..8ae52a6 100644
--- a/providers/bluelock-vcloud-zone01/pom.xml
+++ b/providers/bluelock-vcloud-zone01/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.bluelock-vcloud-zone01.endpoint>https://zone01.bluelock.com/api</test.bluelock-vcloud-zone01.endpoint>
-    <test.bluelock-vcloud-zone01.apiversion>1.0</test.bluelock-vcloud-zone01.apiversion>
+    <test.bluelock-vcloud-zone01.api-version>1.0</test.bluelock-vcloud-zone01.api-version>
+    <test.bluelock-vcloud-zone01.build-version></test.bluelock-vcloud-zone01.build-version>
     <test.bluelock-vcloud-zone01.identity>FIXME_IDENTITY</test.bluelock-vcloud-zone01.identity>
     <test.bluelock-vcloud-zone01.credential>FIXME_CREDENTIAL</test.bluelock-vcloud-zone01.credential>
     <test.bluelock-vcloud-zone01.image-id />
@@ -102,7 +103,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.bluelock-vcloud-zone01.endpoint>${test.bluelock-vcloud-zone01.endpoint}</test.bluelock-vcloud-zone01.endpoint>
-                    <test.bluelock-vcloud-zone01.apiversion>${test.bluelock-vcloud-zone01.apiversion}</test.bluelock-vcloud-zone01.apiversion>
+                    <test.bluelock-vcloud-zone01.api-version>${test.bluelock-vcloud-zone01.api-version}</test.bluelock-vcloud-zone01.api-version>
+                    <test.bluelock-vcloud-zone01.build-version>${test.bluelock-vcloud-zone01.build-version}</test.bluelock-vcloud-zone01.build-version>
                     <test.bluelock-vcloud-zone01.identity>${test.bluelock-vcloud-zone01.identity}</test.bluelock-vcloud-zone01.identity>
                     <test.bluelock-vcloud-zone01.credential>${test.bluelock-vcloud-zone01.credential}</test.bluelock-vcloud-zone01.credential>
                     <test.bluelock-vcloud-zone01.image-id>${test.bluelock-vcloud-zone01.image-id}</test.bluelock-vcloud-zone01.image-id>
diff --git a/providers/cloudfiles-uk/pom.xml b/providers/cloudfiles-uk/pom.xml
index 866ed52..2afc244 100644
--- a/providers/cloudfiles-uk/pom.xml
+++ b/providers/cloudfiles-uk/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.rackspace.cloudfiles.blobstore.integration.CloudFilesUKTestInitializer</test.initializer>
         <test.cloudfiles-uk.endpoint>https://lon.auth.api.rackspacecloud.com</test.cloudfiles-uk.endpoint>
-        <test.cloudfiles-uk.apiversion>1.0</test.cloudfiles-uk.apiversion>
+        <test.cloudfiles-uk.api-version>1.0</test.cloudfiles-uk.api-version>
+        <test.cloudfiles-uk.build-version></test.cloudfiles-uk.build-version>
         <test.cloudfiles-uk.identity>${test.rackspace-uk.identity}</test.cloudfiles-uk.identity>
         <test.cloudfiles-uk.credential>${test.rackspace-uk.credential}</test.cloudfiles-uk.credential>
     </properties>
@@ -109,7 +110,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.cloudfiles-uk.endpoint>${test.cloudfiles-uk.endpoint}</test.cloudfiles-uk.endpoint>
-                                        <test.cloudfiles-uk.apiversion>${test.cloudfiles-uk.apiversion}</test.cloudfiles-uk.apiversion>
+                                        <test.cloudfiles-uk.api-version>${test.cloudfiles-uk.api-version}</test.cloudfiles-uk.api-version>
+                                        <test.cloudfiles-uk.build-version>${test.cloudfiles-uk.build-version}</test.cloudfiles-uk.build-version>
                                         <test.cloudfiles-uk.identity>${test.cloudfiles-uk.identity}</test.cloudfiles-uk.identity>
                                         <test.cloudfiles-uk.credential>${test.cloudfiles-uk.credential}</test.cloudfiles-uk.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/providers/cloudfiles-us/pom.xml b/providers/cloudfiles-us/pom.xml
index 5a1ee41..c7df756 100644
--- a/providers/cloudfiles-us/pom.xml
+++ b/providers/cloudfiles-us/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.rackspace.cloudfiles.blobstore.integration.CloudFilesUSTestInitializer</test.initializer>
         <test.cloudfiles-us.endpoint>https://auth.api.rackspacecloud.com</test.cloudfiles-us.endpoint>
-        <test.cloudfiles-us.apiversion>1.0</test.cloudfiles-us.apiversion>
+        <test.cloudfiles-us.api-version>1.0</test.cloudfiles-us.api-version>
+        <test.cloudfiles-us.build-version></test.cloudfiles-us.build-version>
         <test.cloudfiles-us.identity>${test.rackspace-us.identity}</test.cloudfiles-us.identity>
         <test.cloudfiles-us.credential>${test.rackspace-us.credential}</test.cloudfiles-us.credential>
     </properties>
@@ -109,7 +110,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.cloudfiles-us.endpoint>${test.cloudfiles-us.endpoint}</test.cloudfiles-us.endpoint>
-                                        <test.cloudfiles-us.apiversion>${test.cloudfiles-us.apiversion}</test.cloudfiles-us.apiversion>
+                                        <test.cloudfiles-us.api-version>${test.cloudfiles-us.api-version}</test.cloudfiles-us.api-version>
+                                        <test.cloudfiles-us.build-version>${test.cloudfiles-us.build-version}</test.cloudfiles-us.build-version>
                                         <test.cloudfiles-us.identity>${test.cloudfiles-us.identity}</test.cloudfiles-us.identity>
                                         <test.cloudfiles-us.credential>${test.cloudfiles-us.credential}</test.cloudfiles-us.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/providers/cloudloadbalancers-uk/pom.xml b/providers/cloudloadbalancers-uk/pom.xml
index cff5c9e..6ca7dbd 100644
--- a/providers/cloudloadbalancers-uk/pom.xml
+++ b/providers/cloudloadbalancers-uk/pom.xml
@@ -35,7 +35,8 @@
     
     <properties>
         <test.cloudloadbalancers-uk.endpoint>https://lon.auth.api.rackspacecloud.com</test.cloudloadbalancers-uk.endpoint>
-        <test.cloudloadbalancers-uk.apiversion>1.0</test.cloudloadbalancers-uk.apiversion>
+        <test.cloudloadbalancers-uk.api-version>1.0</test.cloudloadbalancers-uk.api-version>
+        <test.cloudloadbalancers-uk.build-version></test.cloudloadbalancers-uk.build-version>
         <test.cloudloadbalancers-uk.identity>${test.rackspace-uk.identity}</test.cloudloadbalancers-uk.identity>
         <test.cloudloadbalancers-uk.credential>${test.rackspace-uk.credential}</test.cloudloadbalancers-uk.credential>
     </properties>
@@ -109,7 +110,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.cloudloadbalancers-uk.endpoint>${test.cloudloadbalancers-uk.endpoint}</test.cloudloadbalancers-uk.endpoint>
-                                        <test.cloudloadbalancers-uk.apiversion>${test.cloudloadbalancers-uk.apiversion}</test.cloudloadbalancers-uk.apiversion>
+                                        <test.cloudloadbalancers-uk.api-version>${test.cloudloadbalancers-uk.api-version}</test.cloudloadbalancers-uk.api-version>
+                                        <test.cloudloadbalancers-uk.build-version>${test.cloudloadbalancers-uk.build-version}</test.cloudloadbalancers-uk.build-version>
                                         <test.cloudloadbalancers-uk.identity>${test.cloudloadbalancers-uk.identity}</test.cloudloadbalancers-uk.identity>
                                         <test.cloudloadbalancers-uk.credential>${test.cloudloadbalancers-uk.credential}</test.cloudloadbalancers-uk.credential>
                                     </systemPropertyVariables>
diff --git a/providers/cloudloadbalancers-us/pom.xml b/providers/cloudloadbalancers-us/pom.xml
index efc0c4a..d702ea7 100644
--- a/providers/cloudloadbalancers-us/pom.xml
+++ b/providers/cloudloadbalancers-us/pom.xml
@@ -35,7 +35,8 @@
     
     <properties>
         <test.cloudloadbalancers-us.endpoint>https://auth.api.rackspacecloud.com</test.cloudloadbalancers-us.endpoint>
-        <test.cloudloadbalancers-us.apiversion>1.0</test.cloudloadbalancers-us.apiversion>
+        <test.cloudloadbalancers-us.api-version>1.0</test.cloudloadbalancers-us.api-version>
+        <test.cloudloadbalancers-us.build-version></test.cloudloadbalancers-us.build-version>
         <test.cloudloadbalancers-us.identity>${test.rackspace-us.identity}</test.cloudloadbalancers-us.identity>
         <test.cloudloadbalancers-us.credential>${test.rackspace-us.credential}</test.cloudloadbalancers-us.credential>
     </properties>
@@ -109,7 +110,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.cloudloadbalancers-us.endpoint>${test.cloudloadbalancers-us.endpoint}</test.cloudloadbalancers-us.endpoint>
-                                        <test.cloudloadbalancers-us.apiversion>${test.cloudloadbalancers-us.apiversion}</test.cloudloadbalancers-us.apiversion>
+                                        <test.cloudloadbalancers-us.api-version>${test.cloudloadbalancers-us.api-version}</test.cloudloadbalancers-us.api-version>
+                                        <test.cloudloadbalancers-us.build-version>${test.cloudloadbalancers-us.build-version}</test.cloudloadbalancers-us.build-version>
                                         <test.cloudloadbalancers-us.identity>${test.cloudloadbalancers-us.identity}</test.cloudloadbalancers-us.identity>
                                         <test.cloudloadbalancers-us.credential>${test.cloudloadbalancers-us.credential}</test.cloudloadbalancers-us.credential>
                                     </systemPropertyVariables>
diff --git a/providers/cloudonestorage/pom.xml b/providers/cloudonestorage/pom.xml
index 4c43998..14a5fbd 100644
--- a/providers/cloudonestorage/pom.xml
+++ b/providers/cloudonestorage/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.cloudonestorage.blobstore.integration.CloudOneStorageTestInitializer</test.initializer>
         <test.cloudonestorage.endpoint>https://cloudonestorage.peer1.com</test.cloudonestorage.endpoint>
-        <test.cloudonestorage.apiversion>1.3.0</test.cloudonestorage.apiversion>
+        <test.cloudonestorage.api-version>1.3.0</test.cloudonestorage.api-version>
+        <test.cloudonestorage.build-version></test.cloudonestorage.build-version>
         <test.cloudonestorage.identity>FIXME_IDENTITY</test.cloudonestorage.identity>
         <test.cloudonestorage.credential>FIXME_CREDENTIAL</test.cloudonestorage.credential>
     </properties>
@@ -102,7 +103,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.cloudonestorage.endpoint>${test.cloudonestorage.endpoint}</test.cloudonestorage.endpoint>
-                                        <test.cloudonestorage.apiversion>${test.cloudonestorage.apiversion}</test.cloudonestorage.apiversion>
+                                        <test.cloudonestorage.api-version>${test.cloudonestorage.api-version}</test.cloudonestorage.api-version>
+                                        <test.cloudonestorage.build-version>${test.cloudonestorage.build-version}</test.cloudonestorage.build-version>
                                         <test.cloudonestorage.identity>${test.cloudonestorage.identity}</test.cloudonestorage.identity>
                                         <test.cloudonestorage.credential>${test.cloudonestorage.credential}</test.cloudonestorage.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/providers/cloudservers-uk/pom.xml b/providers/cloudservers-uk/pom.xml
index 685c6fc..f9b32e7 100644
--- a/providers/cloudservers-uk/pom.xml
+++ b/providers/cloudservers-uk/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.cloudservers-uk.endpoint>https://lon.auth.api.rackspacecloud.com</test.cloudservers-uk.endpoint>
-    <test.cloudservers-uk.apiversion>1.0</test.cloudservers-uk.apiversion>
+    <test.cloudservers-uk.api-version>1.0</test.cloudservers-uk.api-version>
+    <test.cloudservers-uk.build-version></test.cloudservers-uk.build-version>
     <test.cloudservers-uk.identity>${test.rackspace-uk.identity}</test.cloudservers-uk.identity>
     <test.cloudservers-uk.credential>${test.rackspace-uk.credential}</test.cloudservers-uk.credential>
     <test.cloudservers-uk.image-id />
@@ -107,7 +108,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.cloudservers-uk.endpoint>${test.cloudservers-uk.endpoint}</test.cloudservers-uk.endpoint>
-                    <test.cloudservers-uk.apiversion>${test.cloudservers-uk.apiversion}</test.cloudservers-uk.apiversion>
+                    <test.cloudservers-uk.api-version>${test.cloudservers-uk.api-version}</test.cloudservers-uk.api-version>
+                    <test.cloudservers-uk.build-version>${test.cloudservers-uk.build-version}</test.cloudservers-uk.build-version>
                     <test.cloudservers-uk.identity>${test.cloudservers-uk.identity}</test.cloudservers-uk.identity>
                     <test.cloudservers-uk.credential>${test.cloudservers-uk.credential}</test.cloudservers-uk.credential>
                     <test.cloudservers-uk.image-id>${test.cloudservers-uk.image-id}</test.cloudservers-uk.image-id>
diff --git a/providers/cloudservers-us/pom.xml b/providers/cloudservers-us/pom.xml
index 1fc747a..657d41c 100644
--- a/providers/cloudservers-us/pom.xml
+++ b/providers/cloudservers-us/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.cloudservers-us.endpoint>https://auth.api.rackspacecloud.com</test.cloudservers-us.endpoint>
-    <test.cloudservers-us.apiversion>1.0</test.cloudservers-us.apiversion>
+    <test.cloudservers-us.api-version>1.0</test.cloudservers-us.api-version>
+    <test.cloudservers-us.build-version></test.cloudservers-us.build-version>
     <test.cloudservers-us.identity>${test.rackspace-us.identity}</test.cloudservers-us.identity>
     <test.cloudservers-us.credential>${test.rackspace-us.credential}</test.cloudservers-us.credential>
     <test.cloudservers-us.image-id />
@@ -108,7 +109,8 @@
                   <threadCount>1</threadCount>
                   <systemPropertyVariables>
                     <test.cloudservers-us.endpoint>${test.cloudservers-us.endpoint}</test.cloudservers-us.endpoint>
-                    <test.cloudservers-us.apiversion>${test.cloudservers-us.apiversion}</test.cloudservers-us.apiversion>
+                    <test.cloudservers-us.api-version>${test.cloudservers-us.api-version}</test.cloudservers-us.api-version>
+                    <test.cloudservers-us.build-version>${test.cloudservers-us.build-version}</test.cloudservers-us.build-version>
                     <test.cloudservers-us.identity>${test.cloudservers-us.identity}</test.cloudservers-us.identity>
                     <test.cloudservers-us.credential>${test.cloudservers-us.credential}</test.cloudservers-us.credential>
                     <test.cloudservers-us.image-id>${test.cloudservers-us.image-id}</test.cloudservers-us.image-id>
diff --git a/providers/cloudsigma-lvs/pom.xml b/providers/cloudsigma-lvs/pom.xml
index 073d1b6..c4a66aa 100644
--- a/providers/cloudsigma-lvs/pom.xml
+++ b/providers/cloudsigma-lvs/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.cloudsigma-lvs.endpoint>https://api.lvs.cloudsigma.com</test.cloudsigma-lvs.endpoint>
-    <test.cloudsigma-lvs.apiversion>1.0</test.cloudsigma-lvs.apiversion>
+    <test.cloudsigma-lvs.api-version>1.0</test.cloudsigma-lvs.api-version>
+    <test.cloudsigma-lvs.build-version></test.cloudsigma-lvs.build-version>
     <test.cloudsigma-lvs.identity>FIXME</test.cloudsigma-lvs.identity>
     <test.cloudsigma-lvs.credential>FIXME</test.cloudsigma-lvs.credential>
     <test.cloudsigma-lvs.image-id />
@@ -102,7 +103,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.cloudsigma-lvs.endpoint>${test.cloudsigma-lvs.endpoint}</test.cloudsigma-lvs.endpoint>
-                    <test.cloudsigma-lvs.apiversion>${test.cloudsigma-lvs.apiversion}</test.cloudsigma-lvs.apiversion>
+                    <test.cloudsigma-lvs.api-version>${test.cloudsigma-lvs.api-version}</test.cloudsigma-lvs.api-version>
+                    <test.cloudsigma-lvs.build-version>${test.cloudsigma-lvs.build-version}</test.cloudsigma-lvs.build-version>
                     <test.cloudsigma-lvs.identity>${test.cloudsigma-lvs.identity}</test.cloudsigma-lvs.identity>
                     <test.cloudsigma-lvs.credential>${test.cloudsigma-lvs.credential}</test.cloudsigma-lvs.credential>
                     <test.cloudsigma-lvs.image-id>${test.cloudsigma-lvs.image-id}</test.cloudsigma-lvs.image-id>
diff --git a/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/CloudSigmaLasVegasLondonClientLiveTest.java b/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/CloudSigmaLasVegasLondonClientLiveTest.java
index 024d895..a3cbcd3 100644
--- a/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/CloudSigmaLasVegasLondonClientLiveTest.java
+++ b/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/CloudSigmaLasVegasLondonClientLiveTest.java
@@ -29,6 +29,5 @@
 public class CloudSigmaLasVegasLondonClientLiveTest extends CloudSigmaClientLiveTest {
    public CloudSigmaLasVegasLondonClientLiveTest() {
       provider = "cloudsigma-lvs";
-      bootDrive = "af8bfee4-d249-4d91-b157-b01ee1ce1943";
    }
 }
diff --git a/providers/cloudsigma-zrh/pom.xml b/providers/cloudsigma-zrh/pom.xml
index 5741c2a..0ef7ab4 100644
--- a/providers/cloudsigma-zrh/pom.xml
+++ b/providers/cloudsigma-zrh/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.cloudsigma-zrh.endpoint>https://api.zrh.cloudsigma.com</test.cloudsigma-zrh.endpoint>
-    <test.cloudsigma-zrh.apiversion>1.0</test.cloudsigma-zrh.apiversion>
+    <test.cloudsigma-zrh.api-version>1.0</test.cloudsigma-zrh.api-version>
+    <test.cloudsigma-zrh.build-version></test.cloudsigma-zrh.build-version>
     <test.cloudsigma-zrh.identity>FIXME</test.cloudsigma-zrh.identity>
     <test.cloudsigma-zrh.credential>FIXME</test.cloudsigma-zrh.credential>
     <test.cloudsigma-zrh.image-id />
@@ -102,7 +103,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.cloudsigma-zrh.endpoint>${test.cloudsigma-zrh.endpoint}</test.cloudsigma-zrh.endpoint>
-                    <test.cloudsigma-zrh.apiversion>${test.cloudsigma-zrh.apiversion}</test.cloudsigma-zrh.apiversion>
+                    <test.cloudsigma-zrh.api-version>${test.cloudsigma-zrh.api-version}</test.cloudsigma-zrh.api-version>
+                    <test.cloudsigma-zrh.build-version>${test.cloudsigma-zrh.build-version}</test.cloudsigma-zrh.build-version>
                     <test.cloudsigma-zrh.identity>${test.cloudsigma-zrh.identity}</test.cloudsigma-zrh.identity>
                     <test.cloudsigma-zrh.credential>${test.cloudsigma-zrh.credential}</test.cloudsigma-zrh.credential>
                     <test.cloudsigma-zrh.image-id>${test.cloudsigma-zrh.image-id}</test.cloudsigma-zrh.image-id>
diff --git a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichClientLiveTest.java b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichClientLiveTest.java
index 5ccfc93..d326ee7 100644
--- a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichClientLiveTest.java
+++ b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichClientLiveTest.java
@@ -29,6 +29,5 @@
 public class CloudSigmaZurichClientLiveTest extends CloudSigmaClientLiveTest {
    public CloudSigmaZurichClientLiveTest() {
       provider = "cloudsigma-zrh";
-      bootDrive = "7fad4fe1-daf3-4cb8-a847-082aae4d8506";
    }
 }
diff --git a/providers/elastichosts-lax-p/pom.xml b/providers/elastichosts-lax-p/pom.xml
index ee8ac09..1c7b6cd 100644
--- a/providers/elastichosts-lax-p/pom.xml
+++ b/providers/elastichosts-lax-p/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.elastichosts-lax-p.endpoint>https://api.lax-p.elastichosts.com</test.elastichosts-lax-p.endpoint>
-        <test.elastichosts-lax-p.apiversion>2.0</test.elastichosts-lax-p.apiversion>
+        <test.elastichosts-lax-p.api-version>2.0</test.elastichosts-lax-p.api-version>
+        <test.elastichosts-lax-p.build-version></test.elastichosts-lax-p.build-version>
         <test.elastichosts-lax-p.identity>FIXME_IDENTITY</test.elastichosts-lax-p.identity>
         <test.elastichosts-lax-p.credential>FIXME_CREDENTIAL</test.elastichosts-lax-p.credential>
         <test.elastichosts-lax-p.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.elastichosts-lax-p.endpoint>${test.elastichosts-lax-p.endpoint}</test.elastichosts-lax-p.endpoint>
-                                        <test.elastichosts-lax-p.apiversion>${test.elastichosts-lax-p.apiversion}</test.elastichosts-lax-p.apiversion>
+                                        <test.elastichosts-lax-p.api-version>${test.elastichosts-lax-p.api-version}</test.elastichosts-lax-p.api-version>
+                                        <test.elastichosts-lax-p.build-version>${test.elastichosts-lax-p.build-version}</test.elastichosts-lax-p.build-version>
                                         <test.elastichosts-lax-p.identity>${test.elastichosts-lax-p.identity}</test.elastichosts-lax-p.identity>
                                         <test.elastichosts-lax-p.credential>${test.elastichosts-lax-p.credential}</test.elastichosts-lax-p.credential>
                                         <test.elastichosts-lax-p.image-id>${test.elastichosts-lax-p.image-id}</test.elastichosts-lax-p.image-id>
diff --git a/providers/elastichosts-lax-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesClientLiveTest.java b/providers/elastichosts-lax-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesClientLiveTest.java
index 53e8046..3f86bdb 100644
--- a/providers/elastichosts-lax-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesClientLiveTest.java
+++ b/providers/elastichosts-lax-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesClientLiveTest.java
@@ -25,10 +25,10 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
+@Test(groups = "live", singleThreaded = true, testName = "ElasticHostsPeer1LosAngelesClientLiveTest")
 public class ElasticHostsPeer1LosAngelesClientLiveTest extends ElasticStackClientLiveTest {
    public ElasticHostsPeer1LosAngelesClientLiveTest() {
       provider = "elastichosts-lax-p";
-      bootDrive = "aee5589a-88c3-43ef-bb0a-9cab6e64192d";
    }
+   
 }
diff --git a/providers/elastichosts-lon-b/pom.xml b/providers/elastichosts-lon-b/pom.xml
index 9f50c10..a0cf832 100644
--- a/providers/elastichosts-lon-b/pom.xml
+++ b/providers/elastichosts-lon-b/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.elastichosts-lon-b.endpoint>https://api.lon-b.elastichosts.com</test.elastichosts-lon-b.endpoint>
-        <test.elastichosts-lon-b.apiversion>2.0</test.elastichosts-lon-b.apiversion>
+        <test.elastichosts-lon-b.api-version>2.0</test.elastichosts-lon-b.api-version>
+        <test.elastichosts-lon-b.build-version></test.elastichosts-lon-b.build-version>
         <test.elastichosts-lon-b.identity>FIXME_IDENTITY</test.elastichosts-lon-b.identity>
         <test.elastichosts-lon-b.credential>FIXME_CREDENTIAL</test.elastichosts-lon-b.credential>
         <test.elastichosts-lon-b.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.elastichosts-lon-b.endpoint>${test.elastichosts-lon-b.endpoint}</test.elastichosts-lon-b.endpoint>
-                                        <test.elastichosts-lon-b.apiversion>${test.elastichosts-lon-b.apiversion}</test.elastichosts-lon-b.apiversion>
+                                        <test.elastichosts-lon-b.api-version>${test.elastichosts-lon-b.api-version}</test.elastichosts-lon-b.api-version>
+                                        <test.elastichosts-lon-b.build-version>${test.elastichosts-lon-b.build-version}</test.elastichosts-lon-b.build-version>
                                         <test.elastichosts-lon-b.identity>${test.elastichosts-lon-b.identity}</test.elastichosts-lon-b.identity>
                                         <test.elastichosts-lon-b.credential>${test.elastichosts-lon-b.credential}</test.elastichosts-lon-b.credential>
                                         <test.elastichosts-lon-b.image-id>${test.elastichosts-lon-b.image-id}</test.elastichosts-lon-b.image-id>
diff --git a/providers/elastichosts-lon-b/src/test/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonClientLiveTest.java b/providers/elastichosts-lon-b/src/test/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonClientLiveTest.java
index 906dc00..dc9bbc4 100644
--- a/providers/elastichosts-lon-b/src/test/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonClientLiveTest.java
+++ b/providers/elastichosts-lon-b/src/test/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonClientLiveTest.java
@@ -25,10 +25,9 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
+@Test(groups = "live", singleThreaded = true, testName = "ElasticHostsBlueSquareLondonClientLiveTest")
 public class ElasticHostsBlueSquareLondonClientLiveTest extends ElasticStackClientLiveTest {
    public ElasticHostsBlueSquareLondonClientLiveTest() {
       provider = "elastichosts-lon-b";
-      bootDrive = "aee5589a-88c3-43ef-bb0a-9cab6e64192d";
    }
 }
diff --git a/providers/elastichosts-lon-p/pom.xml b/providers/elastichosts-lon-p/pom.xml
index 3f862a9..ee5b3d6 100644
--- a/providers/elastichosts-lon-p/pom.xml
+++ b/providers/elastichosts-lon-p/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.elastichosts-lon-p.endpoint>https://api.lon-p.elastichosts.com</test.elastichosts-lon-p.endpoint>
-        <test.elastichosts-lon-p.apiversion>2.0</test.elastichosts-lon-p.apiversion>
+        <test.elastichosts-lon-p.api-version>2.0</test.elastichosts-lon-p.api-version>
+        <test.elastichosts-lon-p.build-version></test.elastichosts-lon-p.build-version>
         <test.elastichosts-lon-p.identity>FIXME_IDENTITY</test.elastichosts-lon-p.identity>
         <test.elastichosts-lon-p.credential>FIXME_CREDENTIAL</test.elastichosts-lon-p.credential>
         <test.elastichosts-lon-p.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.elastichosts-lon-p.endpoint>${test.elastichosts-lon-p.endpoint}</test.elastichosts-lon-p.endpoint>
-                                        <test.elastichosts-lon-p.apiversion>${test.elastichosts-lon-p.apiversion}</test.elastichosts-lon-p.apiversion>
+                                        <test.elastichosts-lon-p.api-version>${test.elastichosts-lon-p.api-version}</test.elastichosts-lon-p.api-version>
+                                        <test.elastichosts-lon-p.build-version>${test.elastichosts-lon-p.build-version}</test.elastichosts-lon-p.build-version>
                                         <test.elastichosts-lon-p.identity>${test.elastichosts-lon-p.identity}</test.elastichosts-lon-p.identity>
                                         <test.elastichosts-lon-p.credential>${test.elastichosts-lon-p.credential}</test.elastichosts-lon-p.credential>
                                         <test.elastichosts-lon-p.image-id>${test.elastichosts-lon-p.image-id}</test.elastichosts-lon-p.image-id>
diff --git a/providers/elastichosts-lon-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonClientLiveTest.java b/providers/elastichosts-lon-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonClientLiveTest.java
index a0cbd87..7fe7895 100644
--- a/providers/elastichosts-lon-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonClientLiveTest.java
+++ b/providers/elastichosts-lon-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonClientLiveTest.java
@@ -25,10 +25,9 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
+@Test(groups = "live", singleThreaded = true, testName = "ElasticHostsPeer1LondonClientLiveTest")
 public class ElasticHostsPeer1LondonClientLiveTest extends ElasticStackClientLiveTest {
    public ElasticHostsPeer1LondonClientLiveTest() {
       provider = "elastichosts-lon-p";
-      bootDrive = "aee5589a-88c3-43ef-bb0a-9cab6e64192d";
    }
 }
diff --git a/providers/elastichosts-sat-p/pom.xml b/providers/elastichosts-sat-p/pom.xml
index a60598a..47e5f04 100644
--- a/providers/elastichosts-sat-p/pom.xml
+++ b/providers/elastichosts-sat-p/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.elastichosts-sat-p.endpoint>https://api.sat-p.elastichosts.com</test.elastichosts-sat-p.endpoint>
-        <test.elastichosts-sat-p.apiversion>2.0</test.elastichosts-sat-p.apiversion>
+        <test.elastichosts-sat-p.api-version>2.0</test.elastichosts-sat-p.api-version>
+        <test.elastichosts-sat-p.build-version></test.elastichosts-sat-p.build-version>
         <test.elastichosts-sat-p.identity>FIXME_IDENTITY</test.elastichosts-sat-p.identity>
         <test.elastichosts-sat-p.credential>FIXME_CREDENTIAL</test.elastichosts-sat-p.credential>
         <test.elastichosts-sat-p.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.elastichosts-sat-p.endpoint>${test.elastichosts-sat-p.endpoint}</test.elastichosts-sat-p.endpoint>
-                                        <test.elastichosts-sat-p.apiversion>${test.elastichosts-sat-p.apiversion}</test.elastichosts-sat-p.apiversion>
+                                        <test.elastichosts-sat-p.api-version>${test.elastichosts-sat-p.api-version}</test.elastichosts-sat-p.api-version>
+                                        <test.elastichosts-sat-p.build-version>${test.elastichosts-sat-p.build-version}</test.elastichosts-sat-p.build-version>
                                         <test.elastichosts-sat-p.identity>${test.elastichosts-sat-p.identity}</test.elastichosts-sat-p.identity>
                                         <test.elastichosts-sat-p.credential>${test.elastichosts-sat-p.credential}</test.elastichosts-sat-p.credential>
                                         <test.elastichosts-sat-p.image-id>${test.elastichosts-sat-p.image-id}</test.elastichosts-sat-p.image-id>
diff --git a/providers/elastichosts-sat-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioClientLiveTest.java b/providers/elastichosts-sat-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioClientLiveTest.java
index ce90b54..d4cbc58 100644
--- a/providers/elastichosts-sat-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioClientLiveTest.java
+++ b/providers/elastichosts-sat-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioClientLiveTest.java
@@ -25,10 +25,9 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
+@Test(groups = "live", singleThreaded = true, testName = "ElasticHostsPeer1SanAntonioClientLiveTest")
 public class ElasticHostsPeer1SanAntonioClientLiveTest extends ElasticStackClientLiveTest {
    public ElasticHostsPeer1SanAntonioClientLiveTest() {
       provider = "elastichosts-sat-p";
-      bootDrive = "aee5589a-88c3-43ef-bb0a-9cab6e64192d";
    }
 }
diff --git a/providers/elastichosts-tor-p/pom.xml b/providers/elastichosts-tor-p/pom.xml
index 92a1637..264d821 100644
--- a/providers/elastichosts-tor-p/pom.xml
+++ b/providers/elastichosts-tor-p/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.elastichosts-tor-p.endpoint>https://api.tor-p.elastichosts.com</test.elastichosts-tor-p.endpoint>
-        <test.elastichosts-tor-p.apiversion>2.0</test.elastichosts-tor-p.apiversion>
+        <test.elastichosts-tor-p.api-version>2.0</test.elastichosts-tor-p.api-version>
+        <test.elastichosts-tor-p.build-version></test.elastichosts-tor-p.build-version>
         <test.elastichosts-tor-p.identity>FIXME_IDENTITY</test.elastichosts-tor-p.identity>
         <test.elastichosts-tor-p.credential>FIXME_CREDENTIAL</test.elastichosts-tor-p.credential>
         <test.elastichosts-tor-p.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.elastichosts-tor-p.endpoint>${test.elastichosts-tor-p.endpoint}</test.elastichosts-tor-p.endpoint>
-                                        <test.elastichosts-tor-p.apiversion>${test.elastichosts-tor-p.apiversion}</test.elastichosts-tor-p.apiversion>
+                                        <test.elastichosts-tor-p.api-version>${test.elastichosts-tor-p.api-version}</test.elastichosts-tor-p.api-version>
+                                        <test.elastichosts-tor-p.build-version>${test.elastichosts-tor-p.build-version}</test.elastichosts-tor-p.build-version>
                                         <test.elastichosts-tor-p.identity>${test.elastichosts-tor-p.identity}</test.elastichosts-tor-p.identity>
                                         <test.elastichosts-tor-p.credential>${test.elastichosts-tor-p.credential}</test.elastichosts-tor-p.credential>
                                         <test.elastichosts-tor-p.image-id>${test.elastichosts-tor-p.image-id}</test.elastichosts-tor-p.image-id>
diff --git a/providers/elastichosts-tor-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoClientLiveTest.java b/providers/elastichosts-tor-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoClientLiveTest.java
index 568e2e1..aeeb524 100644
--- a/providers/elastichosts-tor-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoClientLiveTest.java
+++ b/providers/elastichosts-tor-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoClientLiveTest.java
@@ -25,10 +25,9 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
+@Test(groups = "live", singleThreaded = true, testName = "ElasticHostsPeer1TorontoClientLiveTest")
 public class ElasticHostsPeer1TorontoClientLiveTest extends ElasticStackClientLiveTest {
    public ElasticHostsPeer1TorontoClientLiveTest() {
       provider = "elastichosts-tor-p";
-      bootDrive = "aee5589a-88c3-43ef-bb0a-9cab6e64192d";
    }
 }
diff --git a/providers/eucalyptus-partnercloud-ec2/pom.xml b/providers/eucalyptus-partnercloud-ec2/pom.xml
index 4547ed1..e963fda 100644
--- a/providers/eucalyptus-partnercloud-ec2/pom.xml
+++ b/providers/eucalyptus-partnercloud-ec2/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.eucalyptus-partnercloud-ec2.endpoint>http://partnercloud.eucalyptus.com:8773/services/Eucalyptus</test.eucalyptus-partnercloud-ec2.endpoint>
-        <test.eucalyptus-partnercloud-ec2.apiversion>2010-06-15</test.eucalyptus-partnercloud-ec2.apiversion>
+        <test.eucalyptus-partnercloud-ec2.api-version>2010-06-15</test.eucalyptus-partnercloud-ec2.api-version>
+        <test.eucalyptus-partnercloud-ec2.build-version></test.eucalyptus-partnercloud-ec2.build-version>
         <test.eucalyptus-partnercloud-ec2.identity>FIXME_IDENTITY</test.eucalyptus-partnercloud-ec2.identity>
         <test.eucalyptus-partnercloud-ec2.credential>FIXME_CREDENTIAL</test.eucalyptus-partnercloud-ec2.credential>
         <test.eucalyptus-partnercloud-ec2.image-id />
@@ -115,7 +116,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.eucalyptus-partnercloud-ec2.endpoint>${test.eucalyptus-partnercloud-ec2.endpoint}</test.eucalyptus-partnercloud-ec2.endpoint>
-                                        <test.eucalyptus-partnercloud-ec2.apiversion>${test.eucalyptus-partnercloud-ec2.apiversion}</test.eucalyptus-partnercloud-ec2.apiversion>
+                                        <test.eucalyptus-partnercloud-ec2.api-version>${test.eucalyptus-partnercloud-ec2.api-version}</test.eucalyptus-partnercloud-ec2.api-version>
+                                        <test.eucalyptus-partnercloud-ec2.build-version>${test.eucalyptus-partnercloud-ec2.build-version}</test.eucalyptus-partnercloud-ec2.build-version>
                                         <test.eucalyptus-partnercloud-ec2.identity>${test.eucalyptus-partnercloud-ec2.identity}</test.eucalyptus-partnercloud-ec2.identity>
                                         <test.eucalyptus-partnercloud-ec2.credential>${test.eucalyptus-partnercloud-ec2.credential}</test.eucalyptus-partnercloud-ec2.credential>
                                         <test.eucalyptus-partnercloud-ec2.image-id>${test.eucalyptus-partnercloud-ec2.image-id}</test.eucalyptus-partnercloud-ec2.image-id>
diff --git a/providers/eucalyptus-partnercloud-s3/pom.xml b/providers/eucalyptus-partnercloud-s3/pom.xml
index 2aba40b..40e5723 100644
--- a/providers/eucalyptus-partnercloud-s3/pom.xml
+++ b/providers/eucalyptus-partnercloud-s3/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.epc.blobstore.EucalyptusPartnerCloudWalrusTestInitializer</test.initializer>
         <test.eucalyptus-partnercloud-s3.endpoint>http://partnercloud.eucalyptus.com:8773/services/Walrus</test.eucalyptus-partnercloud-s3.endpoint>
-        <test.eucalyptus-partnercloud-s3.apiversion>2006-03-01</test.eucalyptus-partnercloud-s3.apiversion>
+        <test.eucalyptus-partnercloud-s3.api-version>2006-03-01</test.eucalyptus-partnercloud-s3.api-version>
+        <test.eucalyptus-partnercloud-s3.build-version></test.eucalyptus-partnercloud-s3.build-version>
         <test.eucalyptus-partnercloud-s3.identity>FIXME_IDENTITY</test.eucalyptus-partnercloud-s3.identity>
         <test.eucalyptus-partnercloud-s3.credential>FIXME_CREDENTIAL</test.eucalyptus-partnercloud-s3.credential>
         <test.blobstore.container-count>15</test.blobstore.container-count>
@@ -110,7 +111,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.eucalyptus-partnercloud-s3.endpoint>${test.eucalyptus-partnercloud-s3.endpoint}</test.eucalyptus-partnercloud-s3.endpoint>
-                                        <test.eucalyptus-partnercloud-s3.apiversion>${test.eucalyptus-partnercloud-s3.apiversion}</test.eucalyptus-partnercloud-s3.apiversion>
+                                        <test.eucalyptus-partnercloud-s3.api-version>${test.eucalyptus-partnercloud-s3.api-version}</test.eucalyptus-partnercloud-s3.api-version>
+                                        <test.eucalyptus-partnercloud-s3.build-version>${test.eucalyptus-partnercloud-s3.build-version}</test.eucalyptus-partnercloud-s3.build-version>
                                         <test.eucalyptus-partnercloud-s3.identity>${test.eucalyptus-partnercloud-s3.identity}</test.eucalyptus-partnercloud-s3.identity>
                                         <test.eucalyptus-partnercloud-s3.credential>${test.eucalyptus-partnercloud-s3.credential}</test.eucalyptus-partnercloud-s3.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/providers/go2cloud-jhb1/pom.xml b/providers/go2cloud-jhb1/pom.xml
index c3a212f..4efa579 100644
--- a/providers/go2cloud-jhb1/pom.xml
+++ b/providers/go2cloud-jhb1/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.go2cloud-jhb1.endpoint>http://api.jhb1.go2cloud.co.za</test.go2cloud-jhb1.endpoint>
-        <test.go2cloud-jhb1.apiversion>2.0</test.go2cloud-jhb1.apiversion>
+        <test.go2cloud-jhb1.api-version>2.0</test.go2cloud-jhb1.api-version>
+        <test.go2cloud-jhb1.build-version></test.go2cloud-jhb1.build-version>
         <test.go2cloud-jhb1.identity>FIXME_IDENTITY</test.go2cloud-jhb1.identity>
         <test.go2cloud-jhb1.credential>FIXME_CREDENTIAL</test.go2cloud-jhb1.credential>
         <test.go2cloud-jhb1.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.go2cloud-jhb1.endpoint>${test.go2cloud-jhb1.endpoint}</test.go2cloud-jhb1.endpoint>
-                                        <test.go2cloud-jhb1.apiversion>${test.go2cloud-jhb1.apiversion}</test.go2cloud-jhb1.apiversion>
+                                        <test.go2cloud-jhb1.api-version>${test.go2cloud-jhb1.api-version}</test.go2cloud-jhb1.api-version>
+                                        <test.go2cloud-jhb1.build-version>${test.go2cloud-jhb1.build-version}</test.go2cloud-jhb1.build-version>
                                         <test.go2cloud-jhb1.identity>${test.go2cloud-jhb1.identity}</test.go2cloud-jhb1.identity>
                                         <test.go2cloud-jhb1.credential>${test.go2cloud-jhb1.credential}</test.go2cloud-jhb1.credential>
                                         <test.go2cloud-jhb1.image-id>${test.go2cloud-jhb1.image-id}</test.go2cloud-jhb1.image-id>
diff --git a/providers/go2cloud-jhb1/src/test/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ClientLiveTest.java b/providers/go2cloud-jhb1/src/test/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ClientLiveTest.java
index 9d6d9c5..cffecba 100644
--- a/providers/go2cloud-jhb1/src/test/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ClientLiveTest.java
+++ b/providers/go2cloud-jhb1/src/test/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ClientLiveTest.java
@@ -25,10 +25,9 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", singleThreaded = true)
+@Test(groups = "live", singleThreaded = true, testName = "Go2CloudJohannesburg1ClientLiveTest")
 public class Go2CloudJohannesburg1ClientLiveTest extends ElasticStackClientLiveTest {
    public Go2CloudJohannesburg1ClientLiveTest() {
       provider = "go2cloud-jhb1";
-      bootDrive = "14c88d27-1f5e-4ad5-9f3a-28e5d2282f61";
    }
 }
diff --git a/providers/gogrid/pom.xml b/providers/gogrid/pom.xml
index 95396e6..cac7385 100644
--- a/providers/gogrid/pom.xml
+++ b/providers/gogrid/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.gogrid.endpoint>https://api.gogrid.com/api</test.gogrid.endpoint>
-        <test.gogrid.apiversion>1.5</test.gogrid.apiversion>
+        <test.gogrid.api-version>1.5</test.gogrid.api-version>
+        <test.gogrid.build-version></test.gogrid.build-version>
         <test.gogrid.identity>FIXME</test.gogrid.identity>
         <test.gogrid.credential>FIXME</test.gogrid.credential>
         <test.gogrid.image-id />
@@ -93,7 +94,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.gogrid.endpoint>${test.gogrid.endpoint}</test.gogrid.endpoint>
-                                        <test.gogrid.apiversion>${test.gogrid.apiversion}</test.gogrid.apiversion>
+                                        <test.gogrid.api-version>${test.gogrid.api-version}</test.gogrid.api-version>
+                                        <test.gogrid.build-version>${test.gogrid.build-version}</test.gogrid.build-version>
                                         <test.gogrid.identity>${test.gogrid.identity}</test.gogrid.identity>
                                         <test.gogrid.credential>${test.gogrid.credential}</test.gogrid.credential>
                                         <test.gogrid.image-id>${test.gogrid.image-id}</test.gogrid.image-id>
diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/functions/ServerToNodeMetadata.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/functions/ServerToNodeMetadata.java
index ae0abcc..90bfcda 100644
--- a/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/functions/ServerToNodeMetadata.java
+++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/compute/functions/ServerToNodeMetadata.java
@@ -17,7 +17,6 @@
  * under the License.
  */
 package org.jclouds.gogrid.compute.functions;
-
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName;
 
@@ -38,6 +37,7 @@
 import org.jclouds.domain.Location;
 import org.jclouds.gogrid.domain.Server;
 import org.jclouds.gogrid.domain.ServerState;
+import org.jclouds.location.predicates.LocationPredicates;
 import org.jclouds.logging.Logger;
 
 import com.google.common.base.Function;
@@ -57,7 +57,7 @@
    private final Map<ServerState, NodeState> serverStateToNodeState;
    private final Supplier<Set<? extends Image>> images;
    private final Supplier<Set<? extends Hardware>> hardwares;
-   private final Supplier<Map<String, ? extends Location>> locations;
+   private final Supplier<Set<? extends Location>> locations;
 
    static class FindImageForServer implements Predicate<Image> {
       private final Server instance;
@@ -92,7 +92,7 @@
    @Inject
    ServerToNodeMetadata(Map<ServerState, NodeState> serverStateToNodeState,
          @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
-         Supplier<Map<String, ? extends Location>> locations) {
+         @Memoized Supplier<Set<? extends Location>> locations) {
       this.serverStateToNodeState = checkNotNull(serverStateToNodeState, "serverStateToNodeState");
       this.images = checkNotNull(images, "images");
       this.hardwares = checkNotNull(hardwares, "hardwares");
@@ -104,7 +104,8 @@
       NodeMetadataBuilder builder = new NodeMetadataBuilder();
       builder.ids(from.getId() + "");
       builder.name(from.getName());
-      builder.location(locations.get().get(from.getDatacenter().getId() + ""));
+      Location location = Iterables.find(locations.get(), LocationPredicates.idEquals(from.getDatacenter().getId() + ""));
+      builder.location(location);
       builder.group(parseGroupFromName(from.getName()));
       builder.hardware(parseHardware(from));
       builder.imageId(from.getImage().getId() + "");
diff --git a/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridLiveTestDisabled.java b/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridLiveTestDisabled.java
index eca8e81..8648076 100644
--- a/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridLiveTestDisabled.java
+++ b/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridLiveTestDisabled.java
@@ -35,9 +35,7 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.javax.annotation.Nullable;
-
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.domain.Credentials;
 import org.jclouds.gogrid.domain.Ip;
@@ -56,6 +54,7 @@
 import org.jclouds.gogrid.predicates.LoadBalancerLatestJobCompleted;
 import org.jclouds.gogrid.predicates.ServerLatestJobCompleted;
 import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.net.IPSocket;
 import org.jclouds.predicates.InetSocketAddressConnect;
@@ -81,9 +80,11 @@
  * 
  * @author Oleksiy Yarmula
  */
-// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
-@Test(enabled = false, groups = "live", testName = "GoGridLiveTestDisabled")
-public class GoGridLiveTestDisabled {
+@Test(enabled = false, groups = "live", singleThreaded = true, testName = "GoGridLiveTestDisabled")
+public class GoGridLiveTestDisabled extends BaseVersionedServiceLiveTest {
+   public GoGridLiveTestDisabled() {
+      provider = "gogrid";
+   }
 
    private GoGridClient client;
 
@@ -96,32 +97,6 @@
    private List<String> loadBalancersToDeleteAfterTest = new ArrayList<String>();
 
    private RestContext<GoGridClient, GoGridAsyncClient> context;
-   protected String provider = "gogrid";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/functions/ServerToNodeMetadataTest.java b/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/functions/ServerToNodeMetadataTest.java
index f9cc489..a864541 100644
--- a/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/functions/ServerToNodeMetadataTest.java
+++ b/providers/gogrid/src/test/java/org/jclouds/gogrid/compute/functions/ServerToNodeMetadataTest.java
@@ -44,7 +44,6 @@
 import org.testng.annotations.Test;
 
 import com.google.common.base.Suppliers;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
 /**
@@ -73,8 +72,8 @@
       expect(serverStateToNodeState.get(ServerState.ON)).andReturn(NodeState.RUNNING);
 
       Location location = new LocationBuilder().scope(LocationScope.ZONE).id("1").description("US-West-1").build();
-      Map<String, ? extends Location> locations = ImmutableMap.<String, Location> of("1", location);
-
+      Set< ? extends Location> locations = ImmutableSet.< Location> of( location);
+      
       expect(server.getIp()).andReturn(new Ip("127.0.0.1"));
 
       ServerImage image = createMock(ServerImage.class);
@@ -94,7 +93,7 @@
       ServerToNodeMetadata parser = new ServerToNodeMetadata(serverStateToNodeState, Suppliers
                .<Set<? extends Image>> ofInstance(images), Suppliers
                .<Set<? extends Hardware>> ofInstance(GoGridHardwareSupplier.H_ALL), Suppliers
-               .<Map<String, ? extends Location>> ofInstance(locations));
+               .<Set<? extends Location>> ofInstance(locations));
 
       NodeMetadata metadata = parser.apply(server);
       assertEquals(metadata.getLocation(), location);
diff --git a/providers/gogrid/src/test/java/org/jclouds/gogrid/services/BaseGoGridClientLiveTest.java b/providers/gogrid/src/test/java/org/jclouds/gogrid/services/BaseGoGridClientLiveTest.java
index 3a7c7c9..9ca9e9b 100644
--- a/providers/gogrid/src/test/java/org/jclouds/gogrid/services/BaseGoGridClientLiveTest.java
+++ b/providers/gogrid/src/test/java/org/jclouds/gogrid/services/BaseGoGridClientLiveTest.java
@@ -18,10 +18,9 @@
  */
 package org.jclouds.gogrid.services;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.util.Properties;
 
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.gogrid.GoGridAsyncClient;
@@ -41,36 +40,14 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live")
-public class BaseGoGridClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "BaseGoGridClientLiveTest")
+public class BaseGoGridClientLiveTest extends BaseVersionedServiceLiveTest {
+   public BaseGoGridClientLiveTest() {
+      provider = "gogrid";
+   }
 
    protected RestContext<GoGridClient, GoGridAsyncClient> restContext;
-   protected String provider = "gogrid";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    protected ComputeServiceContext context;
-   protected String prefix = System.getProperty("user.name");
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/providers/greenhousedata-element-vcloud/pom.xml b/providers/greenhousedata-element-vcloud/pom.xml
index f1ceac2..8b9c4d1 100644
--- a/providers/greenhousedata-element-vcloud/pom.xml
+++ b/providers/greenhousedata-element-vcloud/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.greenhousedata-element-vcloud.endpoint>https://mycloud.greenhousedata.com/api</test.greenhousedata-element-vcloud.endpoint>
-        <test.greenhousedata-element-vcloud.apiversion>1.0</test.greenhousedata-element-vcloud.apiversion>
+        <test.greenhousedata-element-vcloud.api-version>1.0</test.greenhousedata-element-vcloud.api-version>
+        <test.greenhousedata-element-vcloud.build-version></test.greenhousedata-element-vcloud.build-version>
         <test.greenhousedata-element-vcloud.identity>FIXME_IDENTITY</test.greenhousedata-element-vcloud.identity>
         <test.greenhousedata-element-vcloud.credential>FIXME_CREDENTIAL</test.greenhousedata-element-vcloud.credential>
         <test.greenhousedata-element-vcloud.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.greenhousedata-element-vcloud.endpoint>${test.greenhousedata-element-vcloud.endpoint}</test.greenhousedata-element-vcloud.endpoint>
-                                        <test.greenhousedata-element-vcloud.apiversion>${test.greenhousedata-element-vcloud.apiversion}</test.greenhousedata-element-vcloud.apiversion>
+                                        <test.greenhousedata-element-vcloud.api-version>${test.greenhousedata-element-vcloud.api-version}</test.greenhousedata-element-vcloud.api-version>
+                                        <test.greenhousedata-element-vcloud.build-version>${test.greenhousedata-element-vcloud.build-version}</test.greenhousedata-element-vcloud.build-version>
                                         <test.greenhousedata-element-vcloud.identity>${test.greenhousedata-element-vcloud.identity}</test.greenhousedata-element-vcloud.identity>
                                         <test.greenhousedata-element-vcloud.credential>${test.greenhousedata-element-vcloud.credential}</test.greenhousedata-element-vcloud.credential>
                                         <test.greenhousedata-element-vcloud.image-id>${test.greenhousedata-element-vcloud.image-id}</test.greenhousedata-element-vcloud.image-id>
diff --git a/providers/hpcloud-objectstorage-lvs/pom.xml b/providers/hpcloud-objectstorage-lvs/pom.xml
index be4778e..b287a94 100644
--- a/providers/hpcloud-objectstorage-lvs/pom.xml
+++ b/providers/hpcloud-objectstorage-lvs/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.hpcloud.objectstorage.lvs.blobstore.integration.HPCloudObjectStorageLasVegasTestInitializer</test.initializer>
         <test.hpcloud-objectstorage-lvs.endpoint>https://region-a.geo-1.objects.hpcloudsvc.com/auth</test.hpcloud-objectstorage-lvs.endpoint>
-        <test.hpcloud-objectstorage-lvs.apiversion>1.0</test.hpcloud-objectstorage-lvs.apiversion>
+        <test.hpcloud-objectstorage-lvs.api-version>1.0</test.hpcloud-objectstorage-lvs.api-version>
+        <test.hpcloud-objectstorage-lvs.build-version></test.hpcloud-objectstorage-lvs.build-version>
         <test.hpcloud-objectstorage-lvs.identity>FIXME_IDENTITY</test.hpcloud-objectstorage-lvs.identity>
         <test.hpcloud-objectstorage-lvs.credential>FIXME_CREDENTIAL</test.hpcloud-objectstorage-lvs.credential>        
     </properties>
@@ -111,8 +112,12 @@
                                             <value>${test.hpcloud-objectstorage-lvs.endpoint}</value>
                                         </property>
                                         <property>
-                                            <name>test.hpcloud-objectstorage-lvs.apiversion</name>
-                                            <value>${test.hpcloud-objectstorage-lvs.apiversion}</value>
+                                            <name>test.hpcloud-objectstorage-lvs.api-version</name>
+                                            <value>${test.hpcloud-objectstorage-lvs.api-version}</value>
+                                        </property>
+                                        <property>
+                                            <name>test.hpcloud-objectstorage-lvs.build-version</name>
+                                            <value>${test.hpcloud-objectstorage-lvs.build-version}</value>
                                         </property>
                                         <property>
                                             <name>test.hpcloud-objectstorage-lvs.identity</name>
diff --git a/providers/ninefold-compute/pom.xml b/providers/ninefold-compute/pom.xml
index 6ed69b9..e033a33 100644
--- a/providers/ninefold-compute/pom.xml
+++ b/providers/ninefold-compute/pom.xml
@@ -35,7 +35,8 @@
 
   <properties>
     <test.ninefold-compute.endpoint>https://api.ninefold.com/compute/v1.0/</test.ninefold-compute.endpoint>
-    <test.ninefold-compute.apiversion>2.2.12</test.ninefold-compute.apiversion>
+    <test.ninefold-compute.api-version>2.2.12</test.ninefold-compute.api-version>
+    <test.ninefold-compute.build-version></test.ninefold-compute.build-version>
     <test.ninefold-compute.identity>FIXME_IDENTITY</test.ninefold-compute.identity>
     <test.ninefold-compute.credential>FIXME_CREDENTIAL</test.ninefold-compute.credential>
     <test.ninefold-compute.image-id>1215</test.ninefold-compute.image-id>
@@ -103,7 +104,8 @@
                   <threadCount>1</threadCount>
                   <systemPropertyVariables>
                     <test.ninefold-compute.endpoint>${test.ninefold-compute.endpoint}</test.ninefold-compute.endpoint>
-                    <test.ninefold-compute.apiversion>${test.ninefold-compute.apiversion}</test.ninefold-compute.apiversion>
+                    <test.ninefold-compute.api-version>${test.ninefold-compute.api-version}</test.ninefold-compute.api-version>
+                    <test.ninefold-compute.build-version>${test.ninefold-compute.build-version}</test.ninefold-compute.build-version>
                     <test.ninefold-compute.identity>${test.ninefold-compute.identity}</test.ninefold-compute.identity>
                     <test.ninefold-compute.credential>${test.ninefold-compute.credential}</test.ninefold-compute.credential>
                     <test.ninefold-compute.image-id>${test.ninefold-compute.image-id}</test.ninefold-compute.image-id>
diff --git a/providers/ninefold-storage/pom.xml b/providers/ninefold-storage/pom.xml
index f6e4623..4c5581b 100644
--- a/providers/ninefold-storage/pom.xml
+++ b/providers/ninefold-storage/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.ninefold.storage.blobstore.integration.NinefoldStorageTestInitializer</test.initializer>
         <test.ninefold-storage.endpoint>http://onlinestorage.ninefold.com</test.ninefold-storage.endpoint>
-        <test.ninefold-storage.apiversion>1.4.0</test.ninefold-storage.apiversion>
+        <test.ninefold-storage.api-version>1.4.0</test.ninefold-storage.api-version>
+        <test.ninefold-storage.build-version></test.ninefold-storage.build-version>
         <test.ninefold-storage.identity>FIXME_IDENTITY</test.ninefold-storage.identity>
         <test.ninefold-storage.credential>FIXME_CREDENTIAL</test.ninefold-storage.credential>
     </properties>
@@ -101,7 +102,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.ninefold-storage.endpoint>${test.ninefold-storage.endpoint}</test.ninefold-storage.endpoint>
-                                        <test.ninefold-storage.apiversion>${test.ninefold-storage.apiversion}</test.ninefold-storage.apiversion>
+                                        <test.ninefold-storage.api-version>${test.ninefold-storage.api-version}</test.ninefold-storage.api-version>
+                                        <test.ninefold-storage.build-version>${test.ninefold-storage.build-version}</test.ninefold-storage.build-version>
                                         <test.ninefold-storage.identity>${test.ninefold-storage.identity}</test.ninefold-storage.identity>
                                         <test.ninefold-storage.credential>${test.ninefold-storage.credential}</test.ninefold-storage.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/providers/openhosting-east1/pom.xml b/providers/openhosting-east1/pom.xml
index 9be8296..8abd04a 100644
--- a/providers/openhosting-east1/pom.xml
+++ b/providers/openhosting-east1/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.openhosting-east1.endpoint>https://api.east1.openhosting.com</test.openhosting-east1.endpoint>
-        <test.openhosting-east1.apiversion>2.0</test.openhosting-east1.apiversion>
+        <test.openhosting-east1.api-version>2.0</test.openhosting-east1.api-version>
+        <test.openhosting-east1.build-version></test.openhosting-east1.build-version>
         <test.openhosting-east1.identity>FIXME_IDENTITY</test.openhosting-east1.identity>
         <test.openhosting-east1.credential>FIXME_CREDENTIAL</test.openhosting-east1.credential>
         <test.openhosting-east1.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.openhosting-east1.endpoint>${test.openhosting-east1.endpoint}</test.openhosting-east1.endpoint>
-                                        <test.openhosting-east1.apiversion>${test.openhosting-east1.apiversion}</test.openhosting-east1.apiversion>
+                                        <test.openhosting-east1.api-version>${test.openhosting-east1.api-version}</test.openhosting-east1.api-version>
+                                        <test.openhosting-east1.build-version>${test.openhosting-east1.build-version}</test.openhosting-east1.build-version>
                                         <test.openhosting-east1.identity>${test.openhosting-east1.identity}</test.openhosting-east1.identity>
                                         <test.openhosting-east1.credential>${test.openhosting-east1.credential}</test.openhosting-east1.credential>
                                         <test.openhosting-east1.image-id>${test.openhosting-east1.image-id}</test.openhosting-east1.image-id>
diff --git a/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/OpenHostingEast1ClientLiveTest.java b/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/OpenHostingEast1ClientLiveTest.java
index fef04bc..c1924e3 100644
--- a/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/OpenHostingEast1ClientLiveTest.java
+++ b/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/OpenHostingEast1ClientLiveTest.java
@@ -25,10 +25,9 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
+@Test(groups = "live", singleThreaded = true, testName = "OpenHostingEast1ClientLiveTest")
 public class OpenHostingEast1ClientLiveTest extends ElasticStackClientLiveTest {
    public OpenHostingEast1ClientLiveTest() {
       provider = "openhosting-east1";
-      bootDrive = "8023b089-7b0e-4fcb-8016-01e82f2a9716";
    }
 }
diff --git a/providers/rimuhosting/pom.xml b/providers/rimuhosting/pom.xml
index 5066876..07e2d84 100644
--- a/providers/rimuhosting/pom.xml
+++ b/providers/rimuhosting/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.rimuhosting.endpoint>https://api.rimuhosting.com/r</test.rimuhosting.endpoint>
-        <test.rimuhosting.apiversion>1.0</test.rimuhosting.apiversion>
+        <test.rimuhosting.api-version>1.0</test.rimuhosting.api-version>
+        <test.rimuhosting.build-version></test.rimuhosting.build-version>
         <test.rimuhosting.identity>FIXME</test.rimuhosting.identity>
         <test.rimuhosting.image-id />
     </properties>
@@ -101,7 +102,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.rimuhosting.endpoint>${test.rimuhosting.endpoint}</test.rimuhosting.endpoint>
-                                        <test.rimuhosting.apiversion>${test.rimuhosting.apiversion}</test.rimuhosting.apiversion>
+                                        <test.rimuhosting.api-version>${test.rimuhosting.api-version}</test.rimuhosting.api-version>
+                                        <test.rimuhosting.build-version>${test.rimuhosting.build-version}</test.rimuhosting.build-version>
                                         <test.rimuhosting.identity>${test.rimuhosting.identity}</test.rimuhosting.identity>
                                         <test.rimuhosting.image-id>${test.rimuhosting.image-id}</test.rimuhosting.image-id>
                                     </systemPropertyVariables>
diff --git a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/config/RimuHostingComputeServiceDependenciesModule.java b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/config/RimuHostingComputeServiceDependenciesModule.java
index fff7149..f9142d5 100644
--- a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/config/RimuHostingComputeServiceDependenciesModule.java
+++ b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/compute/config/RimuHostingComputeServiceDependenciesModule.java
@@ -26,8 +26,6 @@
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeState;
 import org.jclouds.compute.internal.ComputeServiceContextImpl;
-import org.jclouds.rest.RestContext;
-import org.jclouds.rest.internal.RestContextImpl;
 import org.jclouds.rimuhosting.miro.RimuHostingAsyncClient;
 import org.jclouds.rimuhosting.miro.RimuHostingClient;
 import org.jclouds.rimuhosting.miro.compute.functions.ServerToNodeMetadata;
@@ -60,9 +58,6 @@
       bind(new TypeLiteral<ComputeServiceContext>() {
       }).to(new TypeLiteral<ComputeServiceContextImpl<RimuHostingClient, RimuHostingAsyncClient>>() {
       }).in(Scopes.SINGLETON);
-      bind(new TypeLiteral<RestContext<RimuHostingClient, RimuHostingAsyncClient>>() {
-      }).to(new TypeLiteral<RestContextImpl<RimuHostingClient, RimuHostingAsyncClient>>() {
-      }).in(Scopes.SINGLETON);
       bind(new TypeLiteral<Function<Server, Iterable<String>>>() {
       }).to(ServerToPublicAddresses.class);
 
diff --git a/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingClientLiveTest.java b/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingClientLiveTest.java
index 818e14e..e63a412 100644
--- a/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingClientLiveTest.java
+++ b/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.rimuhosting.miro;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -26,7 +25,7 @@
 import java.util.Properties;
 import java.util.Set;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.rest.RestContext;
 import org.jclouds.rest.RestContextFactory;
@@ -47,41 +46,21 @@
  * 
  * @author Ivan Meredith
  */
-@Test(groups = "live")
-public class RimuHostingClientLiveTest {
-
+@Test(groups = "live", singleThreaded = true, testName = "RimuHostingClientLiveTest")
+public class RimuHostingClientLiveTest extends BaseVersionedServiceLiveTest {
+   public RimuHostingClientLiveTest() {
+      provider = "rimuhosting";
+   }
+   
    private RimuHostingClient connection;
    private RestContext<RimuHostingClient, RimuHostingAsyncClient> context;
 
-   protected String provider = "rimuhosting";
-   protected String identity;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
       Properties overrides = setupProperties();
 
-      this.context = new RestContextFactory().createContext("rimuhosting", ImmutableSet
+      this.context = new RestContextFactory().createContext(provider, ImmutableSet
                .<Module> of(new Log4JLoggingModule()), overrides);
       this.connection = context.getApi();
 
diff --git a/providers/savvis-symphonyvpdc/pom.xml b/providers/savvis-symphonyvpdc/pom.xml
index af73682..2a74762 100644
--- a/providers/savvis-symphonyvpdc/pom.xml
+++ b/providers/savvis-symphonyvpdc/pom.xml
@@ -49,7 +49,8 @@
     
     <properties>
         <test.savvis-symphonyvpdc.endpoint>https://api.symphonyvpdc.savvis.net/vpdc</test.savvis-symphonyvpdc.endpoint>
-        <test.savvis-symphonyvpdc.apiversion>1.0</test.savvis-symphonyvpdc.apiversion>
+        <test.savvis-symphonyvpdc.api-version>1.0</test.savvis-symphonyvpdc.api-version>
+        <test.savvis-symphonyvpdc.build-version></test.savvis-symphonyvpdc.build-version>
         <test.savvis-symphonyvpdc.identity>FIXME</test.savvis-symphonyvpdc.identity>
         <test.savvis-symphonyvpdc.credential>FIXME</test.savvis-symphonyvpdc.credential>
         <test.savvis-symphonyvpdc.image-id />
@@ -114,7 +115,8 @@
                                 	<threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.savvis-symphonyvpdc.endpoint>${test.savvis-symphonyvpdc.endpoint}</test.savvis-symphonyvpdc.endpoint>
-                                        <test.savvis-symphonyvpdc.apiversion>${test.savvis-symphonyvpdc.apiversion}</test.savvis-symphonyvpdc.apiversion>
+                                        <test.savvis-symphonyvpdc.api-version>${test.savvis-symphonyvpdc.api-version}</test.savvis-symphonyvpdc.api-version>
+                                        <test.savvis-symphonyvpdc.build-version>${test.savvis-symphonyvpdc.build-version}</test.savvis-symphonyvpdc.build-version>
                                         <test.savvis-symphonyvpdc.identity>${test.savvis-symphonyvpdc.identity}</test.savvis-symphonyvpdc.identity>
                                         <test.savvis-symphonyvpdc.credential>${test.savvis-symphonyvpdc.credential}</test.savvis-symphonyvpdc.credential>
                                         <test.savvis-symphonyvpdc.image-id>${test.savvis-symphonyvpdc.image-id}</test.savvis-symphonyvpdc.image-id>
diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java
index 3df5493..693b1b2 100644
--- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java
+++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java
@@ -23,7 +23,7 @@
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
@@ -46,42 +46,28 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live")
-public class BaseVPDCClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "BaseVPDCClientLiveTest")
+public class BaseVPDCClientLiveTest extends BaseVersionedServiceLiveTest {
+   public BaseVPDCClientLiveTest() {
+      provider = "savvis-symphonyvpdc";
+   }
 
    protected RestContext<VPDCClient, VPDCAsyncClient> restContext;
-   protected String provider = "savvis-symphonyvpdc";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
    protected ComputeServiceContext context;
    protected String email;
    protected RetryablePredicate<String> taskTester;
-   protected String prefix = System.getProperty("user.name");
 
+   @Override
    protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
+      super.setupCredentials();
       email = checkNotNull(System.getProperty("test." + VPDCConstants.PROPERTY_VPDC_VDC_EMAIL), "test."
                + VPDCConstants.PROPERTY_VPDC_VDC_EMAIL);
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
    }
-
+   
+   @Override
    protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
+      Properties overrides = super.setupProperties();
       overrides.setProperty(VPDCConstants.PROPERTY_VPDC_VDC_EMAIL, email);
-      if (endpoint != null)
-         overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
-      // TODO savvis uses untrusted certificates, remove these once savvis fixes the issue
-	   overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-	   overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
       return overrides;
    }
 
diff --git a/providers/serverlove-z1-man/pom.xml b/providers/serverlove-z1-man/pom.xml
index cd90b99..9de7ece 100644
--- a/providers/serverlove-z1-man/pom.xml
+++ b/providers/serverlove-z1-man/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.serverlove-z1-man.endpoint>https://api.z1-man.serverlove.com</test.serverlove-z1-man.endpoint>
-        <test.serverlove-z1-man.apiversion>1.0</test.serverlove-z1-man.apiversion>
+        <test.serverlove-z1-man.api-version>1.0</test.serverlove-z1-man.api-version>
+        <test.serverlove-z1-man.build-version></test.serverlove-z1-man.build-version>
         <test.serverlove-z1-man.identity>FIXME_IDENTITY</test.serverlove-z1-man.identity>
         <test.serverlove-z1-man.credential>FIXME_CREDENTIAL</test.serverlove-z1-man.credential>
         <test.serverlove-z1-man.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.serverlove-z1-man.endpoint>${test.serverlove-z1-man.endpoint}</test.serverlove-z1-man.endpoint>
-                                        <test.serverlove-z1-man.apiversion>${test.serverlove-z1-man.apiversion}</test.serverlove-z1-man.apiversion>
+                                        <test.serverlove-z1-man.api-version>${test.serverlove-z1-man.api-version}</test.serverlove-z1-man.api-version>
+                                        <test.serverlove-z1-man.build-version>${test.serverlove-z1-man.build-version}</test.serverlove-z1-man.build-version>
                                         <test.serverlove-z1-man.identity>${test.serverlove-z1-man.identity}</test.serverlove-z1-man.identity>
                                         <test.serverlove-z1-man.credential>${test.serverlove-z1-man.credential}</test.serverlove-z1-man.credential>
                                         <test.serverlove-z1-man.image-id>${test.serverlove-z1-man.image-id}</test.serverlove-z1-man.image-id>
diff --git a/providers/serverlove-z1-man/src/test/java/org/jclouds/serverlove/ServerloveManchesterClientLiveTest.java b/providers/serverlove-z1-man/src/test/java/org/jclouds/serverlove/ServerloveManchesterClientLiveTest.java
index fb76ef7..e7f1ced 100644
--- a/providers/serverlove-z1-man/src/test/java/org/jclouds/serverlove/ServerloveManchesterClientLiveTest.java
+++ b/providers/serverlove-z1-man/src/test/java/org/jclouds/serverlove/ServerloveManchesterClientLiveTest.java
@@ -27,11 +27,10 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", singleThreaded = true)
+@Test(groups = "live", singleThreaded = true, testName = "ServerloveManchesterClientLiveTest")
 public class ServerloveManchesterClientLiveTest extends ElasticStackClientLiveTest {
    public ServerloveManchesterClientLiveTest() {
       provider = "serverlove-z1-man";
-      bootDrive = "574a3921-2926-4a61-bdd9-8d9282b32810";
    }
 
    @Override
diff --git a/providers/skalicloud-sdg-my/pom.xml b/providers/skalicloud-sdg-my/pom.xml
index 659a0a5..915903c 100644
--- a/providers/skalicloud-sdg-my/pom.xml
+++ b/providers/skalicloud-sdg-my/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.skalicloud-sdg-my.endpoint>https://api.sdg-my.skalicloud.com</test.skalicloud-sdg-my.endpoint>
-        <test.skalicloud-sdg-my.apiversion>1.0</test.skalicloud-sdg-my.apiversion>
+        <test.skalicloud-sdg-my.api-version>1.0</test.skalicloud-sdg-my.api-version>
+        <test.skalicloud-sdg-my.build-version></test.skalicloud-sdg-my.build-version>
         <test.skalicloud-sdg-my.identity>FIXME_IDENTITY</test.skalicloud-sdg-my.identity>
         <test.skalicloud-sdg-my.credential>FIXME_CREDENTIAL</test.skalicloud-sdg-my.credential>
         <test.skalicloud-sdg-my.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.skalicloud-sdg-my.endpoint>${test.skalicloud-sdg-my.endpoint}</test.skalicloud-sdg-my.endpoint>
-                                        <test.skalicloud-sdg-my.apiversion>${test.skalicloud-sdg-my.apiversion}</test.skalicloud-sdg-my.apiversion>
+                                        <test.skalicloud-sdg-my.api-version>${test.skalicloud-sdg-my.api-version}</test.skalicloud-sdg-my.api-version>
+                                        <test.skalicloud-sdg-my.build-version>${test.skalicloud-sdg-my.build-version}</test.skalicloud-sdg-my.build-version>
                                         <test.skalicloud-sdg-my.identity>${test.skalicloud-sdg-my.identity}</test.skalicloud-sdg-my.identity>
                                         <test.skalicloud-sdg-my.credential>${test.skalicloud-sdg-my.credential}</test.skalicloud-sdg-my.credential>
                                         <test.skalicloud-sdg-my.image-id>${test.skalicloud-sdg-my.image-id}</test.skalicloud-sdg-my.image-id>
diff --git a/providers/skalicloud-sdg-my/src/test/java/org/jclouds/skalicloud/SkaliCloudMalaysiaClientLiveTest.java b/providers/skalicloud-sdg-my/src/test/java/org/jclouds/skalicloud/SkaliCloudMalaysiaClientLiveTest.java
index 01bd6b3..63e6b9b 100644
--- a/providers/skalicloud-sdg-my/src/test/java/org/jclouds/skalicloud/SkaliCloudMalaysiaClientLiveTest.java
+++ b/providers/skalicloud-sdg-my/src/test/java/org/jclouds/skalicloud/SkaliCloudMalaysiaClientLiveTest.java
@@ -25,10 +25,9 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
+@Test(groups = "live", singleThreaded = true, testName = "SkaliCloudMalaysiaClientLiveTest")
 public class SkaliCloudMalaysiaClientLiveTest extends ElasticStackClientLiveTest {
    public SkaliCloudMalaysiaClientLiveTest() {
       provider = "skalicloud-sdg-my";
-      bootDrive = "3051699a-a536-4220-aeb5-67f2ec101a09";
    }
 }
diff --git a/providers/slicehost/pom.xml b/providers/slicehost/pom.xml
index 2ad2b01..6aa49fc 100644
--- a/providers/slicehost/pom.xml
+++ b/providers/slicehost/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.slicehost.endpoint>https://api.slicehost.com</test.slicehost.endpoint>
-        <test.slicehost.apiversion>1.4.1.1</test.slicehost.apiversion>
+        <test.slicehost.api-version>1.4.1.1</test.slicehost.api-version>
+        <test.slicehost.build-version></test.slicehost.build-version>
         <test.slicehost.identity>FIXME</test.slicehost.identity>
         <test.slicehost.image-id />
     </properties>
@@ -93,7 +94,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.slicehost.endpoint>${test.slicehost.endpoint}</test.slicehost.endpoint>
-                                        <test.slicehost.apiversion>${test.slicehost.apiversion}</test.slicehost.apiversion>
+                                        <test.slicehost.api-version>${test.slicehost.api-version}</test.slicehost.api-version>
+                                        <test.slicehost.build-version>${test.slicehost.build-version}</test.slicehost.build-version>
                                         <test.slicehost.identity>${test.slicehost.identity}</test.slicehost.identity>
                                         <test.slicehost.image-id>${test.slicehost.image-id}</test.slicehost.image-id>
                                     </systemPropertyVariables>
diff --git a/providers/slicehost/src/main/java/org/jclouds/slicehost/compute/strategy/SlicehostComputeServiceAdapter.java b/providers/slicehost/src/main/java/org/jclouds/slicehost/compute/strategy/SlicehostComputeServiceAdapter.java
index 304302c..34fe05e 100644
--- a/providers/slicehost/src/main/java/org/jclouds/slicehost/compute/strategy/SlicehostComputeServiceAdapter.java
+++ b/providers/slicehost/src/main/java/org/jclouds/slicehost/compute/strategy/SlicehostComputeServiceAdapter.java
@@ -1,3 +1,21 @@
+/**
+ * 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.slicehost.compute.strategy;
 
 import static com.google.common.base.Preconditions.checkNotNull;
diff --git a/providers/slicehost/src/test/java/org/jclouds/slicehost/SlicehostClientLiveTest.java b/providers/slicehost/src/test/java/org/jclouds/slicehost/SlicehostClientLiveTest.java
index 0f371cc..b15f7fb 100644
--- a/providers/slicehost/src/test/java/org/jclouds/slicehost/SlicehostClientLiveTest.java
+++ b/providers/slicehost/src/test/java/org/jclouds/slicehost/SlicehostClientLiveTest.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.slicehost;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -30,7 +29,7 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.http.HttpResponseException;
 import org.jclouds.io.Payloads;
@@ -60,36 +59,16 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class SlicehostClientLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "SlicehostClientLiveTest")
+public class SlicehostClientLiveTest extends BaseVersionedServiceLiveTest {
+   public SlicehostClientLiveTest() {
+      provider = "slicehost";
+   }
 
    protected SlicehostClient client;
    protected SshClient.Factory sshFactory;
    private Predicate<IPSocket> socketTester;
 
-   protected String provider = "slicehost";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-            + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
-
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
       setupCredentials();
diff --git a/providers/softlayer/pom.xml b/providers/softlayer/pom.xml
index 19dc914..d1c2f73 100644
--- a/providers/softlayer/pom.xml
+++ b/providers/softlayer/pom.xml
@@ -49,7 +49,8 @@
 
   <properties>
     <test.softlayer.endpoint>https://api.softlayer.com/rest</test.softlayer.endpoint>
-    <test.softlayer.apiversion>3</test.softlayer.apiversion>
+    <test.softlayer.api-version>3</test.softlayer.api-version>
+    <test.softlayer.build-version></test.softlayer.build-version>
     <test.softlayer.identity>FIXME</test.softlayer.identity>
     <test.softlayer.credential>FIXME</test.softlayer.credential>
     <test.softlayer.image-id />
@@ -107,7 +108,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.softlayer.endpoint>${test.softlayer.endpoint}</test.softlayer.endpoint>
-                    <test.softlayer.apiversion>${test.softlayer.apiversion}</test.softlayer.apiversion>
+                    <test.softlayer.api-version>${test.softlayer.api-version}</test.softlayer.api-version>
+                    <test.softlayer.build-version>${test.softlayer.build-version}</test.softlayer.build-version>
                     <test.softlayer.identity>${test.softlayer.identity}</test.softlayer.identity>
                     <test.softlayer.credential>${test.softlayer.credential}</test.softlayer.credential>
                     <test.softlayer.image-id>${test.softlayer.image-id}</test.softlayer.image-id>
diff --git a/providers/softlayer/src/main/java/org/jclouds/softlayer/features/VirtualGuestAsyncClient.java b/providers/softlayer/src/main/java/org/jclouds/softlayer/features/VirtualGuestAsyncClient.java
index 3befe69..8ad1d7d 100644
--- a/providers/softlayer/src/main/java/org/jclouds/softlayer/features/VirtualGuestAsyncClient.java
+++ b/providers/softlayer/src/main/java/org/jclouds/softlayer/features/VirtualGuestAsyncClient.java
@@ -18,13 +18,22 @@
  */
 package org.jclouds.softlayer.features;
 
-import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.http.filters.BasicAuthentication;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.QueryParams;
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
 import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
 import org.jclouds.softlayer.binders.ProductOrderToJson;
@@ -32,9 +41,7 @@
 import org.jclouds.softlayer.domain.ProductOrderReceipt;
 import org.jclouds.softlayer.domain.VirtualGuest;
 
-import javax.ws.rs.*;
-import javax.ws.rs.core.MediaType;
-import java.util.Set;
+import com.google.common.util.concurrent.ListenableFuture;
 
 /**
  * Provides asynchronous access to VirtualGuest via their REST API.
@@ -121,7 +128,7 @@
    @GET
    @Path("/SoftLayer_Billing_Item/{id}/cancelService.json")
    @Consumes(MediaType.APPLICATION_JSON)
-   @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
+   @ExceptionParser(ReturnFalseOnNotFoundOr404.class)
    ListenableFuture<Boolean> cancelService(@PathParam("id") long id);
 
    /**
@@ -141,7 +148,7 @@
    @GET
    @Path("SoftLayer_Virtual_Guest/{id}/getOrderTemplate/MONTHLY.json")
    @Consumes(MediaType.APPLICATION_JSON)
-   @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
    ListenableFuture<ProductOrder> getOrderTemplate(@PathParam("id") long id);
 
 }
diff --git a/providers/softlayer/src/main/java/org/jclouds/softlayer/handlers/SoftLayerErrorHandler.java b/providers/softlayer/src/main/java/org/jclouds/softlayer/handlers/SoftLayerErrorHandler.java
index 8eb9887..d67d41a 100644
--- a/providers/softlayer/src/main/java/org/jclouds/softlayer/handlers/SoftLayerErrorHandler.java
+++ b/providers/softlayer/src/main/java/org/jclouds/softlayer/handlers/SoftLayerErrorHandler.java
@@ -61,8 +61,12 @@
                }
                break;
             case 500:
-               if (message != null && message.indexOf("Unable to determine package for") != -1) {
-                  exception = new ResourceNotFoundException(message, exception);
+               if (message != null ){
+                  if (message.indexOf("Unable to determine package for") != -1) {
+                     exception = new ResourceNotFoundException(message, exception);
+                  } else if (message.indexOf("currently an active transaction") != -1) {
+                     exception = new IllegalStateException(message, exception);
+                  }
                }
          }
       } finally {
diff --git a/providers/softlayer/src/test/java/org/jclouds/softlayer/binders/ProductOrderToJsonTest.java b/providers/softlayer/src/test/java/org/jclouds/softlayer/binders/ProductOrderToJsonTest.java
index c9cbb88..e847b0b 100644
--- a/providers/softlayer/src/test/java/org/jclouds/softlayer/binders/ProductOrderToJsonTest.java
+++ b/providers/softlayer/src/test/java/org/jclouds/softlayer/binders/ProductOrderToJsonTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.binders;
 
 import com.google.common.collect.ImmutableSet;
diff --git a/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerComputeServiceAdapterLiveTest.java b/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerComputeServiceAdapterLiveTest.java
index 8d3ddb4..f663f86 100644
--- a/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerComputeServiceAdapterLiveTest.java
+++ b/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerComputeServiceAdapterLiveTest.java
@@ -77,7 +77,7 @@
 
       guest = adapter.createNodeWithGroupEncodedIntoName(group, name, template);
       assertEquals(guest.getNode().getHostname(), name);
-      assertEquals(guest.getNodeId(), guest.getNode().getId());
+      assertEquals(guest.getNodeId(), guest.getNode().getId() + "");
       assertEquals(guest.getNode().getDomain(), template.getOptions().as(SoftLayerTemplateOptions.class)
             .getDomainName());
       assert InetAddresses.isInetAddress(guest.getNode().getPrimaryBackendIpAddress()) : guest;
diff --git a/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerExperimentLiveTest.java b/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerExperimentLiveTest.java
index 60cf6ac..8dbb18e 100644
--- a/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerExperimentLiveTest.java
+++ b/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/SoftLayerExperimentLiveTest.java
@@ -18,14 +18,13 @@
  */
 package org.jclouds.softlayer.compute;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.sshj.config.SshjSshClientModule;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableSet;
@@ -35,33 +34,21 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", testName = "SoftLayerExperimentLiveTest")
-public class SoftLayerExperimentLiveTest {
-   protected String provider = "softlayer";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+@Test(groups = "live", singleThreaded = true, testName = "SoftLayerExperimentLiveTest")
+public class SoftLayerExperimentLiveTest extends BaseVersionedServiceLiveTest {
+   public SoftLayerExperimentLiveTest() {
+      provider = "softlayer";
    }
 
    @Test
    public void testAndExperiment() {
       ComputeServiceContext context = null;
       try {
-         String identity = checkNotNull(System.getProperty("test.softlayer.identity"), "test.softlayer.identity");
-         String credential = checkNotNull(System.getProperty("test.softlayer.credential"), "test.softlayer.credential");
 
-         context = new ComputeServiceContextFactory().createContext("softlayer", identity, credential, ImmutableSet
+         context = new ComputeServiceContextFactory().createContext(provider, identity, credential, ImmutableSet
                   .<Module> of(new Log4JLoggingModule(), new SshjSshClientModule()));
 
-         assertEquals(context.getComputeService().listAssignableLocations().size(), 7);
+         assertEquals(context.getComputeService().listAssignableLocations().size(), 6);
 
       } finally {
          if (context != null)
@@ -69,4 +56,4 @@
       }
    }
 
-}
\ No newline at end of file
+}
diff --git a/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/functions/ProductItemToImageTest.java b/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/functions/ProductItemToImageTest.java
index 94e7cd5..aca1585 100644
--- a/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/functions/ProductItemToImageTest.java
+++ b/providers/softlayer/src/test/java/org/jclouds/softlayer/compute/functions/ProductItemToImageTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.compute.functions;
 
 import com.google.common.collect.ImmutableSet;
diff --git a/providers/softlayer/src/test/java/org/jclouds/softlayer/features/VirtualGuestClientExpectTest.java b/providers/softlayer/src/test/java/org/jclouds/softlayer/features/VirtualGuestClientExpectTest.java
new file mode 100644
index 0000000..d4c8124
--- /dev/null
+++ b/providers/softlayer/src/test/java/org/jclouds/softlayer/features/VirtualGuestClientExpectTest.java
@@ -0,0 +1,66 @@
+/**
+ * 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.features;
+
+import java.net.URI;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.rest.BaseRestClientExpectTest;
+import org.jclouds.softlayer.SoftLayerClient;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "SoftLayerClientExpectTest")
+public class VirtualGuestClientExpectTest extends BaseRestClientExpectTest<SoftLayerClient> {
+
+
+   public VirtualGuestClientExpectTest() {
+      provider = "softlayer";
+   }
+   
+   public void testCancelGuestReturnsTrueOn200AndFalseOn404() {
+      
+      HttpRequest cancelGuest11 = HttpRequest.builder().method("GET")
+               .endpoint(URI.create("https://api.softlayer.com/rest/v3/SoftLayer_Billing_Item/11/cancelService.json"))
+               .headers(
+               ImmutableMultimap.<String, String> builder()
+               .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==")
+               .put("Accept", "application/json").build()).build();
+
+      HttpResponse found = HttpResponse.builder().statusCode(200).build();
+
+      SoftLayerClient clientWhenServiceExists = requestSendsResponse(cancelGuest11, found);
+      
+      assert clientWhenServiceExists.getVirtualGuestClient().cancelService(11l);
+
+
+      HttpResponse notFound = HttpResponse.builder().statusCode(404).build();
+
+      SoftLayerClient clientWhenServiceDoesntExist = requestSendsResponse(cancelGuest11, notFound);
+      
+      assert !clientWhenServiceDoesntExist.getVirtualGuestClient().cancelService(11l);
+
+   }
+}
\ No newline at end of file
diff --git a/providers/softlayer/src/test/java/org/jclouds/softlayer/SoftLayerErrorHandlerTest.java b/providers/softlayer/src/test/java/org/jclouds/softlayer/handlers/SoftLayerErrorHandlerTest.java
similarity index 90%
rename from providers/softlayer/src/test/java/org/jclouds/softlayer/SoftLayerErrorHandlerTest.java
rename to providers/softlayer/src/test/java/org/jclouds/softlayer/handlers/SoftLayerErrorHandlerTest.java
index c30b7b7..1a083a5 100644
--- a/providers/softlayer/src/test/java/org/jclouds/softlayer/SoftLayerErrorHandlerTest.java
+++ b/providers/softlayer/src/test/java/org/jclouds/softlayer/handlers/SoftLayerErrorHandlerTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.softlayer;
+package org.jclouds.softlayer.handlers;
 
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.reportMatcher;
@@ -56,6 +56,17 @@
             "{\"error\":\"Unable to determine package for 'node2102835255.me.org'.\"}",
             ResourceNotFoundException.class);
    }
+   
+   @Test
+   public void test500MakesIllegalStateExceptionOnActiveTransaction() {
+      assertCodeMakes(
+            "GET",
+            URI.create("https://api.softlayer.com/rest/v3/SoftLayer_Billing_Item/8676376/cancelService.json"),
+            500,
+            "",
+            "{\"error\":\"There is currently an active transaction.\"}",
+            IllegalStateException.class);
+   }
 
    @Test
    public void test401MakesAuthorizationException() {
diff --git a/providers/stratogen-vcloud-mycloud/pom.xml b/providers/stratogen-vcloud-mycloud/pom.xml
index 9f03b32..e354845 100644
--- a/providers/stratogen-vcloud-mycloud/pom.xml
+++ b/providers/stratogen-vcloud-mycloud/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.stratogen-vcloud-mycloud.endpoint>https://vcd.stratogen.net/api</test.stratogen-vcloud-mycloud.endpoint>
-        <test.stratogen-vcloud-mycloud.apiversion>1.0</test.stratogen-vcloud-mycloud.apiversion>
+        <test.stratogen-vcloud-mycloud.api-version>1.0</test.stratogen-vcloud-mycloud.api-version>
+        <test.stratogen-vcloud-mycloud.build-version></test.stratogen-vcloud-mycloud.build-version>
         <test.stratogen-vcloud-mycloud.identity>FIXME_IDENTITY</test.stratogen-vcloud-mycloud.identity>
         <test.stratogen-vcloud-mycloud.credential>FIXME_CREDENTIAL</test.stratogen-vcloud-mycloud.credential>
         <test.stratogen-vcloud-mycloud.image-id />
@@ -100,7 +101,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.stratogen-vcloud-mycloud.endpoint>${test.stratogen-vcloud-mycloud.endpoint}</test.stratogen-vcloud-mycloud.endpoint>
-                                        <test.stratogen-vcloud-mycloud.apiversion>${test.stratogen-vcloud-mycloud.apiversion}</test.stratogen-vcloud-mycloud.apiversion>
+                                        <test.stratogen-vcloud-mycloud.api-version>${test.stratogen-vcloud-mycloud.api-version}</test.stratogen-vcloud-mycloud.api-version>
+                                        <test.stratogen-vcloud-mycloud.build-version>${test.stratogen-vcloud-mycloud.build-version}</test.stratogen-vcloud-mycloud.build-version>
                                         <test.stratogen-vcloud-mycloud.identity>${test.stratogen-vcloud-mycloud.identity}</test.stratogen-vcloud-mycloud.identity>
                                         <test.stratogen-vcloud-mycloud.credential>${test.stratogen-vcloud-mycloud.credential}</test.stratogen-vcloud-mycloud.credential>
                                         <test.stratogen-vcloud-mycloud.image-id>${test.stratogen-vcloud-mycloud.image-id}</test.stratogen-vcloud-mycloud.image-id>
diff --git a/providers/synaptic-storage/pom.xml b/providers/synaptic-storage/pom.xml
index 82d16c3..8edeaa6 100644
--- a/providers/synaptic-storage/pom.xml
+++ b/providers/synaptic-storage/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.synaptic.storage.blobstore.integration.SynapticStorageTestInitializer</test.initializer>
         <test.synaptic-storage.endpoint>https://storage.synaptic.att.com</test.synaptic-storage.endpoint>
-        <test.synaptic-storage.apiversion>1.3.0</test.synaptic-storage.apiversion>
+        <test.synaptic-storage.api-version>1.3.0</test.synaptic-storage.api-version>
+        <test.synaptic-storage.build-version></test.synaptic-storage.build-version>
         <test.synaptic-storage.identity>FIXME_IDENTITY</test.synaptic-storage.identity>
         <test.synaptic-storage.credential>FIXME_CREDENTIAL</test.synaptic-storage.credential>
     </properties>
@@ -101,7 +102,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.synaptic-storage.endpoint>${test.synaptic-storage.endpoint}</test.synaptic-storage.endpoint>
-                                        <test.synaptic-storage.apiversion>${test.synaptic-storage.apiversion}</test.synaptic-storage.apiversion>
+                                        <test.synaptic-storage.api-version>${test.synaptic-storage.api-version}</test.synaptic-storage.api-version>
+                                        <test.synaptic-storage.build-version>${test.synaptic-storage.build-version}</test.synaptic-storage.build-version>
                                         <test.synaptic-storage.identity>${test.synaptic-storage.identity}</test.synaptic-storage.identity>
                                         <test.synaptic-storage.credential>${test.synaptic-storage.credential}</test.synaptic-storage.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/providers/trmk-ecloud/pom.xml b/providers/trmk-ecloud/pom.xml
index 4122f6b..517aadb 100644
--- a/providers/trmk-ecloud/pom.xml
+++ b/providers/trmk-ecloud/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.trmk-ecloud.endpoint>https://services.enterprisecloud.terremark.com/api</test.trmk-ecloud.endpoint>
         <test.trmk-ecloud.datacenter>MIA</test.trmk-ecloud.datacenter>
-        <test.trmk-ecloud.apiversion>0.8b-ext2.8</test.trmk-ecloud.apiversion>
+        <test.trmk-ecloud.api-version>0.8b-ext2.8</test.trmk-ecloud.api-version>
+        <test.trmk-ecloud.build-version></test.trmk-ecloud.build-version>
         <test.trmk-ecloud.identity>FIXME</test.trmk-ecloud.identity>
         <test.trmk-ecloud.credential>FIXME</test.trmk-ecloud.credential>
         <test.trmk-ecloud.image-id />
@@ -102,7 +103,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.trmk-ecloud.endpoint>${test.trmk-ecloud.endpoint}</test.trmk-ecloud.endpoint>
-                                        <test.trmk-ecloud.apiversion>${test.trmk-ecloud.apiversion}</test.trmk-ecloud.apiversion>
+                                        <test.trmk-ecloud.api-version>${test.trmk-ecloud.api-version}</test.trmk-ecloud.api-version>
+                                        <test.trmk-ecloud.build-version>${test.trmk-ecloud.build-version}</test.trmk-ecloud.build-version>
                                         <test.trmk-ecloud.identity>${test.trmk-ecloud.identity}</test.trmk-ecloud.identity>
                                         <test.trmk-ecloud.credential>${test.trmk-ecloud.credential}</test.trmk-ecloud.credential>
                                         <test.trmk-ecloud.datacenter>${test.trmk-ecloud.datacenter}</test.trmk-ecloud.datacenter>
diff --git a/providers/trmk-vcloudexpress/pom.xml b/providers/trmk-vcloudexpress/pom.xml
index a306150..13ca895 100644
--- a/providers/trmk-vcloudexpress/pom.xml
+++ b/providers/trmk-vcloudexpress/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.trmk-vcloudexpress.endpoint>https://services.vcloudexpress.terremark.com/api</test.trmk-vcloudexpress.endpoint>
-        <test.trmk-vcloudexpress.apiversion>0.8a-ext1.6</test.trmk-vcloudexpress.apiversion>
+        <test.trmk-vcloudexpress.api-version>0.8a-ext1.6</test.trmk-vcloudexpress.api-version>
+        <test.trmk-vcloudexpress.build-version></test.trmk-vcloudexpress.build-version>
         <test.trmk-vcloudexpress.identity>FIXME</test.trmk-vcloudexpress.identity>
         <test.trmk-vcloudexpress.credential>FIXME</test.trmk-vcloudexpress.credential>
         <test.trmk-vcloudexpress.image-id />
@@ -101,7 +102,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.trmk-vcloudexpress.endpoint>${test.trmk-vcloudexpress.endpoint}</test.trmk-vcloudexpress.endpoint>
-                                        <test.trmk-vcloudexpress.apiversion>${test.trmk-vcloudexpress.apiversion}</test.trmk-vcloudexpress.apiversion>
+                                        <test.trmk-vcloudexpress.api-version>${test.trmk-vcloudexpress.api-version}</test.trmk-vcloudexpress.api-version>
+                                        <test.trmk-vcloudexpress.build-version>${test.trmk-vcloudexpress.build-version}</test.trmk-vcloudexpress.build-version>
                                         <test.trmk-vcloudexpress.identity>${test.trmk-vcloudexpress.identity}</test.trmk-vcloudexpress.identity>
                                         <test.trmk-vcloudexpress.credential>${test.trmk-vcloudexpress.credential}</test.trmk-vcloudexpress.credential>
                                         <test.trmk-vcloudexpress.image-id>${test.trmk-vcloudexpress.image-id}</test.trmk-vcloudexpress.image-id>
diff --git a/providers/trmk-vcloudexpress/src/test/java/org/jclouds/trmk/vcloudexpress/InternetServiceLiveTest.java b/providers/trmk-vcloudexpress/src/test/java/org/jclouds/trmk/vcloudexpress/InternetServiceLiveTest.java
index e48acf0..46fa7d5 100644
--- a/providers/trmk-vcloudexpress/src/test/java/org/jclouds/trmk/vcloudexpress/InternetServiceLiveTest.java
+++ b/providers/trmk-vcloudexpress/src/test/java/org/jclouds/trmk/vcloudexpress/InternetServiceLiveTest.java
@@ -18,15 +18,13 @@
  */
 package org.jclouds.trmk.vcloudexpress;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.net.URI;
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
-import org.jclouds.Constants;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.rest.RestContext;
@@ -47,8 +45,12 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true)
-public class InternetServiceLiveTest {
+@Test(groups = "live", singleThreaded = true, testName = "InternetServiceLiveTest")
+public class InternetServiceLiveTest extends BaseVersionedServiceLiveTest {
+   public InternetServiceLiveTest() {
+      provider = "trmk-vcloudexpress";
+   }
+
    TerremarkVCloudExpressClient tmClient;
 
    private Set<InternetService> services = Sets.newLinkedHashSet();
@@ -89,31 +91,6 @@
       delete(services);
    }
 
-   protected String provider = "trmk-vcloudexpress";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
-               + ".credential");
-      endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-               + ".apiversion");
-   }
-
-   protected Properties setupProperties() {
-      Properties overrides = new Properties();
-      overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true");
-      overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true");
-      overrides.setProperty(provider + ".identity", identity);
-      overrides.setProperty(provider + ".credential", credential);
-      overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
-      return overrides;
-   }
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
diff --git a/resources/NOTICE.txt b/resources/NOTICE.txt
index eef2bb3..03b1527 100644
--- a/resources/NOTICE.txt
+++ b/resources/NOTICE.txt
@@ -18,7 +18,7 @@
 ====
 
 jclouds
-Copyright 2009-2011 jclouds, Inc.
+Copyright 2009-2012 jclouds, Inc.
 
 This product includes software developed at
 jclouds, Inc. (http://www.jclouds.org/).
@@ -32,27 +32,19 @@
 This product includes net.oauth.core 
 distributed under the Apache Software License, Version 2.0.
 
-net.oauth.signature.pem.PKCS1EncodedPublicKeySpec is taken from net.oauth.core
-distributed under the Apache Software License, Version 2.0.
-(c) 1998-2009 AOL LLC
-
 This product includes Gson (http://code.google.com/p/google-gson)
 distributed under the Apache Software License, Version 2.0.
 
-com.google.gson.stream.JsonWriter patches the same class taken from Gson
-distributed under the Apache Software License, Version 2.0.
-(c) 2010 Google Inc.
-
-com.google.gson.ObjectMapTypeAdapter contains pieces of Gson's MapTypeAdapter
-distributed under the Apache Software License, Version 2.0.
-(c) 2010 Google Inc.
-
 This product includes Guice (http://code.google.com/p/google-guice)
 distributed under the Apache Software License, Version 2.0.
 
 This product includes Guava (http://code.google.com/p/guava-libraries)
 distributed under the Apache Software License, Version 2.0.
 
+org.jclouds.util.Suppliers2 and Suppliers2Test contain pieces of Guava's Suppliers
+distributed under the Apache Software License, Version 2.0.
+(c) 2010 Google Inc.
+
 This product includes BouncyCastle (http://www.bouncycastle.org/licence.html)
 distributed with the following notice:
   "Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
diff --git a/sandbox-apis/elb/pom.xml b/sandbox-apis/elb/pom.xml
index fb57564..f1fdd1c 100644
--- a/sandbox-apis/elb/pom.xml
+++ b/sandbox-apis/elb/pom.xml
@@ -36,12 +36,14 @@
     <properties>
         <test.elb.zone>us-east-1a</test.elb.zone>
         <test.elb.endpoint>https://elasticloadbalancing.us-east-1.amazonaws.com</test.elb.endpoint>
-        <test.elb.apiversion>2010-07-01</test.elb.apiversion>
+        <test.elb.api-version>2010-07-01</test.elb.api-version>
+        <test.elb.build-version></test.elb.build-version>
         <test.elb.identity>${test.aws.identity}</test.elb.identity>
         <test.elb.credential>${test.aws.credential}</test.elb.credential>
         <test.elb.compute.provider>ec2</test.elb.compute.provider>
         <test.elb.compute.endpoint>https://ec2.us-east-1.amazonaws.com</test.elb.compute.endpoint>
-        <test.elb.compute.apiversion>2010-06-15</test.elb.compute.apiversion>
+        <test.elb.compute.api-version>2010-06-15</test.elb.compute.api-version>
+        <test.elb.compute.build-version></test.elb.compute.build-version>
         <test.elb.compute.identity>${test.aws.identity}</test.elb.compute.identity>
         <test.elb.compute.credential>${test.aws.credential}</test.elb.compute.credential>
         <test.elb.compute.image-id></test.elb.compute.image-id>
@@ -113,12 +115,14 @@
                                     <systemPropertyVariables>
                                         <test.elb.zone>${test.elb.zone}</test.elb.zone>
                                         <test.elb.endpoint>${test.elb.endpoint}</test.elb.endpoint>
-                                        <test.elb.apiversion>${test.elb.apiversion}</test.elb.apiversion>
+                                        <test.elb.api-version>${test.elb.api-version}</test.elb.api-version>
+                                        <test.elb.build-version>${test.elb.build-version}</test.elb.build-version>
                                         <test.elb.identity>${test.elb.identity}</test.elb.identity>
                                         <test.elb.credential>${test.elb.credential}</test.elb.credential>
                                         <test.elb.compute.provider>${test.elb.compute.provider}</test.elb.compute.provider>
                                         <test.elb.compute.endpoint>${test.elb.compute.endpoint}</test.elb.compute.endpoint>
-                                        <test.elb.compute.apiversion>${test.elb.compute.apiversion}</test.elb.compute.apiversion>
+                                        <test.elb.compute.api-version>${test.elb.compute.api-version}</test.elb.compute.api-version>
+                                        <test.elb.compute.build-version>${test.elb.compute.build-version}</test.elb.compute.build-version>
                                         <test.elb.compute.identity>${test.elb.compute.identity}</test.elb.compute.identity>
                                         <test.elb.compute.credential>${test.elb.compute.credential}</test.elb.compute.credential>
                                         <test.elb.compute.image-id>${test.elb.compute.image-id}</test.elb.compute.image-id>
diff --git a/sandbox-apis/elb/src/test/java/org/jclouds/elb/ELBClientLiveTest.java b/sandbox-apis/elb/src/test/java/org/jclouds/elb/ELBClientLiveTest.java
index 8f8f285..78c5e8b 100644
--- a/sandbox-apis/elb/src/test/java/org/jclouds/elb/ELBClientLiveTest.java
+++ b/sandbox-apis/elb/src/test/java/org/jclouds/elb/ELBClientLiveTest.java
@@ -50,7 +50,7 @@
    protected String identity;
    protected String credential;
    protected String endpoint;
-   protected String apiversion;
+   protected String apiVersion;
    protected String name = "TestLoadBalancer";
 
    protected void setupCredentials() {
@@ -58,7 +58,7 @@
       credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
             + ".credential");
       endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+     apiVersion = System.getProperty("test." + provider + ".api-version");
    }
 
    protected Properties setupProperties() {
@@ -70,8 +70,8 @@
          overrides.setProperty(provider + ".credential", credential);
       if (endpoint != null)
          overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
+      if (apiVersion != null)
+         overrides.setProperty(provider + ".api-version", apiVersion);
       return overrides;
    }
 
diff --git a/sandbox-apis/libvirt/pom.xml b/sandbox-apis/libvirt/pom.xml
index 7a07bfa..398cf61 100644
--- a/sandbox-apis/libvirt/pom.xml
+++ b/sandbox-apis/libvirt/pom.xml
@@ -36,7 +36,8 @@
         <!-- when instances are hung, open a ticket and add here -->
         <jclouds.compute.blacklist.nodes>trmkrun-ccc,test.trmk-924</jclouds.compute.blacklist.nodes>
         <test.libvirt.endpoint>test:///default</test.libvirt.endpoint>
-        <test.libvirt.apiversion>1.0</test.libvirt.apiversion>
+        <test.libvirt.api-version>1.0</test.libvirt.api-version>
+        <test.libvirt.build-version></test.libvirt.build-version>
         <test.libvirt.identity>FIXME</test.libvirt.identity>
         <test.libvirt.credential>FIXME</test.libvirt.credential>
         <test.libvirt.image-id></test.libvirt.image-id>
@@ -126,7 +127,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.libvirt.endpoint>${test.libvirt.endpoint}</test.libvirt.endpoint>
-                                        <test.libvirt.apiversion>${test.libvirt.apiversion}</test.libvirt.apiversion>
+                                        <test.libvirt.api-version>${test.libvirt.api-version}</test.libvirt.api-version>
+                                        <test.libvirt.build-version>${test.libvirt.build-version}</test.libvirt.build-version>
                                         <test.libvirt.identity>${test.libvirt.identity}</test.libvirt.identity>
                                         <test.libvirt.credential>${test.libvirt.credential}</test.libvirt.credential>
                                         <test.libvirt.image-id>${test.libvirt.image-id}</test.libvirt.image-id>
diff --git a/sandbox-apis/libvirt/src/test/java/org/jclouds/libvirt/compute/LibvirtExperimentLiveTest.java b/sandbox-apis/libvirt/src/test/java/org/jclouds/libvirt/compute/LibvirtExperimentLiveTest.java
index f49e9c4..3820811 100644
--- a/sandbox-apis/libvirt/src/test/java/org/jclouds/libvirt/compute/LibvirtExperimentLiveTest.java
+++ b/sandbox-apis/libvirt/src/test/java/org/jclouds/libvirt/compute/LibvirtExperimentLiveTest.java
@@ -40,14 +40,14 @@
    protected String identity;
    protected String credential;
    protected String endpoint;
-   protected String apiversion;
+   protected String apiVersion;
 
    @BeforeClass
    protected void setupCredentials() {
       identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
       credential = System.getProperty("test." + provider + ".credential");
       endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+     apiVersion = System.getProperty("test." + provider + ".api-version");
    }
 
    @Test
@@ -92,4 +92,4 @@
       }
    }
 
-}
\ No newline at end of file
+}
diff --git a/sandbox-apis/nirvanix/pom.xml b/sandbox-apis/nirvanix/pom.xml
index 1c07d59..85063ba 100644
--- a/sandbox-apis/nirvanix/pom.xml
+++ b/sandbox-apis/nirvanix/pom.xml
@@ -46,7 +46,8 @@
 
     <properties>
         <test.nirvanix.endpoint>http://services.nirvanix.com</test.nirvanix.endpoint>
-        <test.nirvanix.apiversion>2.5.6</test.nirvanix.apiversion>
+        <test.nirvanix.api-version>2.5.6</test.nirvanix.api-version>
+        <test.nirvanix.build-version></test.nirvanix.build-version>
         <test.nirvanix.identity>FIXME</test.nirvanix.identity>
         <test.nirvanix.credential> FIXME </test.nirvanix.credential>
     </properties>
@@ -106,7 +107,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.nirvanix.endpoint>${test.nirvanix.endpoint}</test.nirvanix.endpoint>
-                                        <test.nirvanix.apiversion>${test.nirvanix.apiversion}</test.nirvanix.apiversion>
+                                        <test.nirvanix.api-version>${test.nirvanix.api-version}</test.nirvanix.api-version>
+                                        <test.nirvanix.build-version>${test.nirvanix.build-version}</test.nirvanix.build-version>
                                         <test.nirvanix.identity>${test.nirvanix.identity}</test.nirvanix.identity>
                                         <test.nirvanix.credential>${test.nirvanix.credential}</test.nirvanix.credential>
                                     </systemPropertyVariables>
diff --git a/sandbox-apis/openstack-nova/pom.xml b/sandbox-apis/openstack-nova/pom.xml
index 7c912a1..9660cb2 100644
--- a/sandbox-apis/openstack-nova/pom.xml
+++ b/sandbox-apis/openstack-nova/pom.xml
@@ -35,7 +35,8 @@
 

   <properties>

     <test.openstack-nova.endpoint>http://localhost:8774/v1.1/</test.openstack-nova.endpoint>

-    <test.openstack-nova.apiversion>v1.1</test.openstack-nova.apiversion>

+    <test.openstack-nova.api-version>v1.1</test.openstack-nova.api-version>

+    <test.openstack-nova.build-version></test.openstack-nova.build-version>

     <test.openstack-nova.identity>FIXME_IDENTITY</test.openstack-nova.identity>

     <test.openstack-nova.credential>FIXME_CREDENTIALS</test.openstack-nova.credential>

     <test.openstack-nova.image-id></test.openstack-nova.image-id>

@@ -115,7 +116,8 @@
                 <configuration>

                   <systemPropertyVariables>

                     <test.openstack-nova.endpoint>${test.openstack-nova.endpoint}</test.openstack-nova.endpoint>

-                    <test.openstack-nova.apiversion>${test.openstack-nova.apiversion}</test.openstack-nova.apiversion>

+                    <test.openstack-nova.api-version>${test.openstack-nova.api-version}</test.openstack-nova.api-version>

+                    <test.openstack-nova.build-version>${test.openstack-nova.build-version}</test.openstack-nova.build-version>

                     <test.openstack-nova.identity>${test.openstack-nova.identity}</test.openstack-nova.identity>

                     <test.openstack-nova.credential>${test.openstack-nova.credential}</test.openstack-nova.credential>

                     <test.openstack-nova.image-id>${test.openstack-nova.image-id}</test.openstack-nova.image-id>

diff --git a/sandbox-apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaPropertiesBuilder.java b/sandbox-apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaPropertiesBuilder.java
index 6c744a9..3edac5e 100644
--- a/sandbox-apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaPropertiesBuilder.java
+++ b/sandbox-apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaPropertiesBuilder.java
@@ -37,7 +37,7 @@
    @Override
    protected Properties defaultProperties() {
       Properties properties = super.defaultProperties();
-      properties.setProperty(PROPERTY_ENDPOINT, "http://localhost:8774/{apiversion}/{identity}");
+      properties.setProperty(PROPERTY_ENDPOINT, "http://localhost:8774/{api-version}/{identity}");
       properties.setProperty(PROPERTY_API_VERSION, "v1.1");
       return properties;
    }
@@ -47,7 +47,7 @@
    }
 
    public static final Pattern IDENTITY_PATTERN = Pattern.compile("\\{identity\\}");
-   public static final Pattern API_VERSION_PATTERN = Pattern.compile("\\{apiversion\\}");
+   public static final Pattern API_VERSION_PATTERN = Pattern.compile("\\{api-version\\}");
 
    @Override
    public Properties build() {
diff --git a/sandbox-apis/pcs/pom.xml b/sandbox-apis/pcs/pom.xml
index 6589e51..dc5ecbd 100644
--- a/sandbox-apis/pcs/pom.xml
+++ b/sandbox-apis/pcs/pom.xml
@@ -46,7 +46,8 @@
 
     <properties>
         <test.pcs.endpoint>FIXME</test.pcs.endpoint>
-        <test.pcs.apiversion>2</test.pcs.apiversion>
+        <test.pcs.api-version>2</test.pcs.api-version>
+        <test.pcs.build-version></test.pcs.build-version>
         <test.pcs.identity>FIXME</test.pcs.identity>
         <test.pcs.credential>FIXME</test.pcs.credential>
     </properties>
@@ -106,7 +107,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.pcs.endpoint>${test.pcs.endpoint}</test.pcs.endpoint>
-                                        <test.pcs.apiversion>${test.pcs.apiversion}</test.pcs.apiversion>
+                                        <test.pcs.api-version>${test.pcs.api-version}</test.pcs.api-version>
+                                        <test.pcs.build-version>${test.pcs.build-version}</test.pcs.build-version>
                                         <test.pcs.identity>${test.pcs.identity}</test.pcs.identity>
                                         <test.pcs.credential>${test.pcs.credential}</test.pcs.credential>
                                     </systemPropertyVariables>
diff --git a/sandbox-apis/pcs/src/test/java/org/jclouds/mezeo/pcs/PCSClientLiveTest.java b/sandbox-apis/pcs/src/test/java/org/jclouds/mezeo/pcs/PCSClientLiveTest.java
index 2621943..337f47b 100644
--- a/sandbox-apis/pcs/src/test/java/org/jclouds/mezeo/pcs/PCSClientLiveTest.java
+++ b/sandbox-apis/pcs/src/test/java/org/jclouds/mezeo/pcs/PCSClientLiveTest.java
@@ -82,14 +82,14 @@
    protected String identity;
    protected String credential;
    protected String endpoint;
-   protected String apiversion;
+   protected String apiVersion;
 
    protected void setupCredentials() {
       identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
       credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
             + ".credential");
       endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+     apiVersion = System.getProperty("test." + provider + ".api-version");
    }
 
    protected Properties setupProperties() {
@@ -100,8 +100,8 @@
       overrides.setProperty(provider + ".credential", credential);
       if (endpoint != null)
          overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
+      if (apiVersion != null)
+         overrides.setProperty(provider + ".api-version", apiVersion);
       return overrides;
    }
 
diff --git a/sandbox-apis/scality-rs2/pom.xml b/sandbox-apis/scality-rs2/pom.xml
index 1f8e37d..d11e695 100644
--- a/sandbox-apis/scality-rs2/pom.xml
+++ b/sandbox-apis/scality-rs2/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.scality.rs2.blobstore.ScalityRS2TestInitializer</test.initializer>
         <test.scality-rs2.endpoint>FIXME_ENDPOINT</test.scality-rs2.endpoint>
-        <test.scality-rs2.apiversion>2006-03-01</test.scality-rs2.apiversion>
+        <test.scality-rs2.api-version>2006-03-01</test.scality-rs2.api-version>
+        <test.scality-rs2.build-version></test.scality-rs2.build-version>
         <test.scality-rs2.identity>FIXME_IDENTITY</test.scality-rs2.identity>
         <test.scality-rs2.credential>FIXME_CREDENTIAL</test.scality-rs2.credential>
     </properties>
@@ -101,7 +102,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.scality-rs2.endpoint>${test.scality-rs2.endpoint}</test.scality-rs2.endpoint>
-                                        <test.scality-rs2.apiversion>${test.scality-rs2.apiversion}</test.scality-rs2.apiversion>
+                                        <test.scality-rs2.api-version>${test.scality-rs2.api-version}</test.scality-rs2.api-version>
+                                        <test.scality-rs2.build-version>${test.scality-rs2.build-version}</test.scality-rs2.build-version>
                                         <test.scality-rs2.identity>${test.scality-rs2.identity}</test.scality-rs2.identity>
                                         <test.scality-rs2.credential>${test.scality-rs2.credential}</test.scality-rs2.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/sandbox-apis/simpledb/pom.xml b/sandbox-apis/simpledb/pom.xml
index ac40645..d0ffb06 100644
--- a/sandbox-apis/simpledb/pom.xml
+++ b/sandbox-apis/simpledb/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.simpledb.endpoint>https://sdb.amazonaws.com</test.simpledb.endpoint>
-        <test.simpledb.apiversion>2009-04-15</test.simpledb.apiversion>
+        <test.simpledb.api-version>2009-04-15</test.simpledb.api-version>
+        <test.simpledb.build-version></test.simpledb.build-version>
         <test.simpledb.identity>${test.aws.identity}</test.simpledb.identity>
         <test.simpledb.credential>${test.aws.credential}</test.simpledb.credential>
     </properties>
@@ -79,7 +80,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.simpledb.endpoint>${test.simpledb.endpoint}</test.simpledb.endpoint>
-                                        <test.simpledb.apiversion>${test.simpledb.apiversion}</test.simpledb.apiversion>
+                                        <test.simpledb.api-version>${test.simpledb.api-version}</test.simpledb.api-version>
+                                        <test.simpledb.build-version>${test.simpledb.build-version}</test.simpledb.build-version>
                                         <test.simpledb.identity>${test.simpledb.identity}</test.simpledb.identity>
                                         <test.simpledb.credential>${test.simpledb.credential}</test.simpledb.credential>
                                     </systemPropertyVariables>
diff --git a/sandbox-apis/simpledb/src/test/java/org/jclouds/simpledb/SimpleDBClientLiveTest.java b/sandbox-apis/simpledb/src/test/java/org/jclouds/simpledb/SimpleDBClientLiveTest.java
index 7783be8..dc60197 100644
--- a/sandbox-apis/simpledb/src/test/java/org/jclouds/simpledb/SimpleDBClientLiveTest.java
+++ b/sandbox-apis/simpledb/src/test/java/org/jclouds/simpledb/SimpleDBClientLiveTest.java
@@ -55,14 +55,14 @@
    protected String identity;
    protected String credential;
    protected String endpoint;
-   protected String apiversion;
+   protected String apiVersion;
 
    protected void setupCredentials() {
       identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
       credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
             + ".credential");
       endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+     apiVersion = System.getProperty("test." + provider + ".api-version");
    }
 
    protected Properties setupProperties() {
@@ -74,8 +74,8 @@
          overrides.setProperty(provider + ".credential", credential);
       if (endpoint != null)
          overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
+      if (apiVersion != null)
+         overrides.setProperty(provider + ".api-version", apiVersion);
       return overrides;
    }
 
diff --git a/sandbox-apis/sqs/pom.xml b/sandbox-apis/sqs/pom.xml
index 6c70773..bad642f 100644
--- a/sandbox-apis/sqs/pom.xml
+++ b/sandbox-apis/sqs/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.sqs.endpoint>https://sqs.us-east-1.amazonaws.com</test.sqs.endpoint>
-        <test.sqs.apiversion>2009-02-01</test.sqs.apiversion>
+        <test.sqs.api-version>2009-02-01</test.sqs.api-version>
+        <test.sqs.build-version></test.sqs.build-version>
         <test.sqs.identity>${test.aws.identity}</test.sqs.identity>
         <test.sqs.credential>${test.aws.credential}</test.sqs.credential>
     </properties>
@@ -79,7 +80,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.sqs.endpoint>${test.sqs.endpoint}</test.sqs.endpoint>
-                                        <test.sqs.apiversion>${test.sqs.apiversion}</test.sqs.apiversion>
+                                        <test.sqs.api-version>${test.sqs.api-version}</test.sqs.api-version>
+                                        <test.sqs.build-version>${test.sqs.build-version}</test.sqs.build-version>
                                         <test.sqs.identity>${test.sqs.identity}</test.sqs.identity>
                                         <test.sqs.credential>${test.sqs.credential}</test.sqs.credential>
                                     </systemPropertyVariables>
diff --git a/sandbox-apis/sqs/src/test/java/org/jclouds/sqs/SQSClientLiveTest.java b/sandbox-apis/sqs/src/test/java/org/jclouds/sqs/SQSClientLiveTest.java
index 2b5139d..2ebff95 100644
--- a/sandbox-apis/sqs/src/test/java/org/jclouds/sqs/SQSClientLiveTest.java
+++ b/sandbox-apis/sqs/src/test/java/org/jclouds/sqs/SQSClientLiveTest.java
@@ -60,14 +60,14 @@
    protected String identity;
    protected String credential;
    protected String endpoint;
-   protected String apiversion;
+   protected String apiVersion;
 
    protected void setupCredentials() {
       identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
       credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
             + ".credential");
       endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+     apiVersion = System.getProperty("test." + provider + ".api-version");
    }
 
    protected Properties setupProperties() {
@@ -79,8 +79,8 @@
          overrides.setProperty(provider + ".credential", credential);
       if (endpoint != null)
          overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
+      if (apiVersion != null)
+         overrides.setProperty(provider + ".api-version", apiVersion);
       return overrides;
    }
 
diff --git a/sandbox-apis/virtualbox/README.md b/sandbox-apis/virtualbox/README.md
index 85aa024..73bf6bb 100644
--- a/sandbox-apis/virtualbox/README.md
+++ b/sandbox-apis/virtualbox/README.md
@@ -13,8 +13,8 @@
 1.  Create a working dir on 'user.home' called by default "jclouds-virtualbox-test" 
 (this value can be overwritten directly on the commandline using -Dtest.virtualbox.workingDir)
 2.  Install Virtualbox from the internet (mac os x lion and ubuntu host are supported at the moment) 
-3.  Download by default an ubuntu 11.04 server i386 ISO into "jclouds-virtualbox-test" from http://releases.ubuntu.com/11.04/ubuntu-11.04-server-i386.iso.  
-4.  Download VirtualBox Guest Additions ISO (tested with VBoxGuestAdditions_4.0.2-update-69551.iso) into "jclouds-virtualbox-test"
+3.  Download by default an ubuntu 10.04.3 server i386 ISO into "jclouds-virtualbox-test" from http://releases.ubuntu.com/10.04/ubuntu-10.04.3-server-i386.iso
+4.  Download VirtualBox Guest Additions ISO into "jclouds-virtualbox-test" from http://download.virtualbox.org/virtualbox/4.1.8/VBoxGuestAdditions_4.1.8.iso
 5.  Disable login credential: $ VBoxManage setproperty websrvauthlibrary null
 6.  Start an embedded jetty server that serves a preseed file specifically written for ubuntu 11.04
 7.  Start webservice with increasead timeout: $ /usr/bin/vboxwebsrv --timeout 10000 and then will:
@@ -36,4 +36,4 @@
 and choose the numberOfVirtualMachine you need using -Dtest.virtualbox.numberOfVirtualMachine=<#ofVMs>,
 These VMs will be cloned starting from the golden template VM created before
 
-It will create a "numberOfVirtualMachine" in a vbox default machine folder with 'VMName_i' name, by cloning the golden template in linked mode with a bridged NIC.
\ No newline at end of file
+It will create a "numberOfVirtualMachine" in a vbox default machine folder with 'VMName_i' name, by cloning the golden template in linked mode with a bridged NIC.
diff --git a/sandbox-apis/virtualbox/pom.xml b/sandbox-apis/virtualbox/pom.xml
index 313d503..f4b3a57 100644
--- a/sandbox-apis/virtualbox/pom.xml
+++ b/sandbox-apis/virtualbox/pom.xml
@@ -35,12 +35,13 @@
 
   <properties>
     <test.virtualbox.endpoint>http://localhost:18083/</test.virtualbox.endpoint>
-    <test.virtualbox.apiversion>4.1.4</test.virtualbox.apiversion>
+    <test.virtualbox.api-version>4.1.4</test.virtualbox.api-version>
+    <test.virtualbox.build-version>4.1.8r75467</test.virtualbox.build-version>
     <test.virtualbox.identity>administrator</test.virtualbox.identity>
     <test.virtualbox.credential>12345</test.virtualbox.credential>
-    <test.virtualbox.image-id></test.virtualbox.image-id>
-    <test.virtualbox.image.login-user></test.virtualbox.image.login-user>
-    <test.virtualbox.image.authenticate-sudo></test.virtualbox.image.authenticate-sudo>
+    <test.virtualbox.image-id>ubuntu-11.04-server-i386</test.virtualbox.image-id>
+    <test.virtualbox.image.login-user>toor:password</test.virtualbox.image.login-user>
+    <test.virtualbox.image.authenticate-sudo>true</test.virtualbox.image.authenticate-sudo>
   </properties>
 
   <dependencies>
@@ -52,7 +53,7 @@
     <dependency>
       <groupId>org.virtualbox</groupId>
       <artifactId>vboxjws</artifactId>
-      <version>${test.virtualbox.apiversion}</version>
+      <version>${test.virtualbox.api-version}</version>
     </dependency>
     <dependency>
       <groupId>org.jclouds.api</groupId>
@@ -67,7 +68,6 @@
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-server</artifactId>
-      <version>7.4.0.RC0</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
@@ -102,12 +102,6 @@
       <version>0.9.29</version>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.jclouds.driver</groupId>
-      <artifactId>jclouds-log4j</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
   
   <profiles>
@@ -129,7 +123,8 @@
                   <threadCount>1</threadCount>
                   <systemPropertyVariables>
                     <test.virtualbox.endpoint>${test.virtualbox.endpoint}</test.virtualbox.endpoint>
-                    <test.virtualbox.apiversion>${test.virtualbox.apiversion}</test.virtualbox.apiversion>
+                    <test.virtualbox.api-version>${test.virtualbox.api-version}</test.virtualbox.api-version>
+                    <test.virtualbox.build-version>${test.virtualbox.build-version}</test.virtualbox.build-version>
                     <test.virtualbox.identity>${test.virtualbox.identity}</test.virtualbox.identity>
                     <test.virtualbox.credential>${test.virtualbox.credential}</test.virtualbox.credential>
                     <test.virtualbox.image-id>${test.virtualbox.image-id}</test.virtualbox.image-id>
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKey.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/Preconfiguration.java
similarity index 63%
rename from scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKey.java
rename to sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/Preconfiguration.java
index 43978f0..03bbcbe 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InstallRSAPrivateKey.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/Preconfiguration.java
@@ -16,17 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.scriptbuilder.domain;
+package org.jclouds.virtualbox;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
 
 /**
- * @see org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey
+ * Relating to a preseed or KickStart source
+ * 
  * @author Adrian Cole
+ * 
  */
-@Deprecated
-public class InstallRSAPrivateKey extends org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey {
-
-   public InstallRSAPrivateKey(String privateKey) {
-      super(privateKey);
-   }
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
+@Qualifier
+public @interface Preconfiguration {
 
 }
\ No newline at end of file
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxContextBuilder.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxContextBuilder.java
index 5a1ce56..1b7d6d4 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxContextBuilder.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxContextBuilder.java
@@ -24,8 +24,8 @@
 
 import org.jclouds.compute.StandaloneComputeServiceContextBuilder;
 import org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule;
-import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Supplier;
 import com.google.inject.Module;
 
 /**
@@ -33,10 +33,11 @@
  * 
  * @author Mattias Holmqvist, Andrea Turli
  */
-public class VirtualBoxContextBuilder extends StandaloneComputeServiceContextBuilder<VirtualBoxManager> {
+@SuppressWarnings("unchecked")
+public class VirtualBoxContextBuilder extends StandaloneComputeServiceContextBuilder<Supplier> {
 
    public VirtualBoxContextBuilder(Properties properties) {
-      super(VirtualBoxManager.class, properties);
+      super(Supplier.class, properties);
    }
 
    @Override
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java
index 91877c3..7b2014b 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java
@@ -19,6 +19,8 @@
 
 package org.jclouds.virtualbox;
 
+import static org.jclouds.Constants.PROPERTY_API_VERSION;
+import static org.jclouds.Constants.PROPERTY_BUILD_VERSION;
 import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
 import static org.jclouds.Constants.PROPERTY_ENDPOINT;
 import static org.jclouds.Constants.PROPERTY_IDENTITY;
@@ -27,7 +29,8 @@
 import java.util.Properties;
 
 import org.jclouds.PropertiesBuilder;
-import org.jclouds.virtualbox.config.VirtualBoxConstants;
+import static org.jclouds.compute.reference.ComputeServiceConstants.*;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.*;
 
 /**
  * Builds properties for VirtualBox integration.
@@ -47,32 +50,34 @@
    @Override
    protected Properties defaultProperties() {
       Properties properties = super.defaultProperties();
+      properties.put(PROPERTY_ENDPOINT, "http://localhost:18083/");
+      // later version not in maven, yet
+      properties.put(PROPERTY_API_VERSION, "4.1.4");
+      properties.put(PROPERTY_BUILD_VERSION, "4.1.8r75467");
       properties.put(PROPERTY_IDENTITY, "administrator");
       properties.put(PROPERTY_CREDENTIAL, "12345");
-      properties.put(PROPERTY_ENDPOINT, "http://localhost:18083/");
-      properties.put(VirtualBoxConstants.VIRTUALBOX_PRESEED_URL, "http://dl.dropbox.com/u/693111/preseed.cfg");
-      properties.put(VirtualBoxConstants.VIRTUALBOX_SNAPSHOT_DESCRIPTION, "jclouds-virtualbox-snaphot");
-      properties.put(VirtualBoxConstants.VIRTUALBOX_HOSTNAME, "jclouds-virtualbox-kickstart-admin");
-      properties
-            .put(VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE,
-                  "<Esc><Esc><Enter> "
-                        + "/install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg "
-                        + "debian-installer=en_US auto locale=en_US kbd-chooser/method=us "
-                        + "hostname="
-                        + properties.get(VirtualBoxConstants.VIRTUALBOX_HOSTNAME)
-                        + " "
-                        + "fb=false debconf/frontend=noninteractive "
-                        + "keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false "
-                        + "initrd=/install/initrd.gz -- <Enter>");
 
-      properties.put(VirtualBoxConstants.VIRTUALBOX_WORKINGDIR, System.getProperty("user.home") + File.separator
-            + System.getProperty("test.virtualbox.workingDir", "jclouds-virtualbox-test"));
+      properties.put(PROPERTY_IMAGE_ID, "ubuntu-11.04-server-i386");
+      properties.put(PROPERTY_IMAGE_LOGIN_USER, "toor:password");
+      properties.put(PROPERTY_IMAGE_AUTHENTICATE_SUDO, "true");
 
-      // TODO: Add more properties and use the wired properties from test code.
-      properties.put(VirtualBoxConstants.VIRTUALBOX_DISTRO_ISO_NAME, "ubuntu-11.04-server-i386.iso");
+      properties.put(VIRTUALBOX_ISO_URL, "http://releases.ubuntu.com/11.04/ubuntu-11.04-server-i386.iso");
+      properties.put(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE, "<Esc><Esc><Enter> "
+               + "/install/vmlinuz noapic preseed/url=PRECONFIGURATION_URL "
+               + "debian-installer=en_US auto locale=en_US kbd-chooser/method=us " + "hostname=" + "HOSTNAME "
+               + "fb=false debconf/frontend=noninteractive "
+               + "keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false "
+               + "initrd=/install/initrd.gz -- <Enter>");
+      
+      properties.put(VIRTUALBOX_WORKINGDIR, System.getProperty("user.home") + File.separator
+               + System.getProperty("test.virtualbox.workingDir", "jclouds-virtualbox-test"));
 
-      properties.put(VirtualBoxConstants.VIRTUALBOX_JETTY_PORT, "8080");
+      properties.put(VIRTUALBOX_PRECONFIGURATION_URL, "http://10.0.2.2:8080/src/test/resources/preseed.cfg");
+      
+      
+
 
       return properties;
    }
+
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java
index 3fb4ac2..0207063 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java
@@ -24,8 +24,6 @@
 import static com.google.common.collect.Iterables.transform;
 import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
 
-import java.util.Collections;
-
 import javax.inject.Inject;
 
 import org.jclouds.compute.ComputeServiceAdapter;
@@ -43,7 +41,9 @@
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
 import com.google.common.base.Throwables;
+import com.google.common.collect.Iterables;
 import com.google.inject.Singleton;
 
 /**
@@ -56,12 +56,12 @@
 @Singleton
 public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IMachine, IMachine, Image, Location> {
 
-   private final VirtualBoxManager manager;
+   private final Supplier<VirtualBoxManager> manager;
    private final JustProvider justProvider;
-   private Function<IMachine, Image> iMachineToImage;
+   private final Function<IMachine, Image> iMachineToImage;
 
    @Inject
-   public VirtualBoxComputeServiceAdapter(VirtualBoxManager manager, JustProvider justProvider,
+   public VirtualBoxComputeServiceAdapter(Supplier<VirtualBoxManager> manager, JustProvider justProvider,
          Function<IMachine, Image> iMachineToImage) {
       this.iMachineToImage = iMachineToImage;
       this.manager = checkNotNull(manager, "manager");
@@ -76,24 +76,35 @@
 
    @Override
    public Iterable<IMachine> listNodes() {
-      return Collections.emptyList();
+      return Iterables.filter(manager.get().getVBox().getMachines(), new Predicate<IMachine>() {
+
+         @Override
+         public boolean apply(IMachine arg0) {
+            return !arg0.getName().startsWith(VIRTUALBOX_IMAGE_PREFIX);
+         }
+
+      });
    }
 
    @Override
    public Iterable<IMachine> listHardwareProfiles() {
-      return manager.getVBox().getMachines();
+      return imageMachines();
    }
 
    @Override
    public Iterable<Image> listImages() {
+      return transform(imageMachines(), iMachineToImage);
+   }
+
+   private Iterable<IMachine> imageMachines() {
       final Predicate<? super IMachine> imagePredicate = new Predicate<IMachine>() {
          @Override
          public boolean apply(@Nullable IMachine iMachine) {
             return iMachine.getName().startsWith(VIRTUALBOX_IMAGE_PREFIX);
          }
       };
-      final Iterable<IMachine> imageMachines = filter(manager.getVBox().getMachines(), imagePredicate);
-      return transform(imageMachines, iMachineToImage);
+      final Iterable<IMachine> imageMachines = filter(manager.get().getVBox().getMachines(), imagePredicate);
+      return imageMachines;
    }
 
    @SuppressWarnings("unchecked")
@@ -104,29 +115,29 @@
 
    @Override
    public IMachine getNode(String vmName) {
-      return manager.getVBox().findMachine(vmName);
+      return manager.get().getVBox().findMachine(vmName);
    }
 
    @Override
    public void destroyNode(String vmName) {
-      IMachine machine = manager.getVBox().findMachine(vmName);
+      IMachine machine = manager.get().getVBox().findMachine(vmName);
       powerDownMachine(machine);
       machine.unregister(CleanupMode.Full);
    }
 
    @Override
    public void rebootNode(String vmName) {
-      IMachine machine = manager.getVBox().findMachine(vmName);
+      IMachine machine = manager.get().getVBox().findMachine(vmName);
       powerDownMachine(machine);
-      launchVMProcess(machine, manager.getSessionObject());
+      launchVMProcess(machine, manager.get().getSessionObject());
    }
 
    @Override
    public void resumeNode(String vmName) {
-      IMachine machine = manager.getVBox().findMachine(vmName);
+      IMachine machine = manager.get().getVBox().findMachine(vmName);
       ISession machineSession;
       try {
-         machineSession = manager.openMachineSession(machine);
+         machineSession = manager.get().openMachineSession(machine);
          machineSession.getConsole().resume();
          machineSession.unlockMachine();
       } catch (Exception e) {
@@ -136,10 +147,10 @@
 
    @Override
    public void suspendNode(String vmName) {
-      IMachine machine = manager.getVBox().findMachine(vmName);
+      IMachine machine = manager.get().getVBox().findMachine(vmName);
       ISession machineSession;
       try {
-         machineSession = manager.openMachineSession(machine);
+         machineSession = manager.get().openMachineSession(machine);
          machineSession.getConsole().pause();
          machineSession.unlockMachine();
       } catch (Exception e) {
@@ -161,7 +172,7 @@
 
    private void powerDownMachine(IMachine machine) {
       try {
-         ISession machineSession = manager.openMachineSession(machine);
+         ISession machineSession = manager.get().openMachineSession(machine);
          IProgress progress = machineSession.getConsole().powerDown();
          progress.waitForCompletion(-1);
          machineSession.unlockMachine();
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java
index 6f3274f..27fbc46 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java
@@ -19,13 +19,16 @@
 
 package org.jclouds.virtualbox.config;
 
+import java.io.InputStream;
 import java.net.URI;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 
-import javax.inject.Named;
 import javax.inject.Singleton;
 
-import org.jclouds.Constants;
+import org.jclouds.byon.Node;
+import org.jclouds.byon.functions.NodeToNodeMetadata;
+import org.jclouds.byon.suppliers.SupplyFromProviderURIOrNodesProperty;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
 import org.jclouds.compute.domain.Hardware;
@@ -34,21 +37,34 @@
 import org.jclouds.compute.domain.NodeState;
 import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
 import org.jclouds.domain.Location;
 import org.jclouds.functions.IdentityFunction;
-import org.jclouds.location.Provider;
 import org.jclouds.location.suppliers.OnlyLocationOrFirstZone;
+import org.jclouds.predicates.RetryablePredicate;
+import org.jclouds.ssh.SshClient;
+import org.jclouds.virtualbox.Preconfiguration;
 import org.jclouds.virtualbox.compute.VirtualBoxComputeServiceAdapter;
+import org.jclouds.virtualbox.domain.ExecutionType;
 import org.jclouds.virtualbox.functions.IMachineToHardware;
 import org.jclouds.virtualbox.functions.IMachineToImage;
 import org.jclouds.virtualbox.functions.IMachineToNodeMetadata;
+import org.jclouds.virtualbox.functions.IMachineToSshClient;
+import org.jclouds.virtualbox.functions.admin.StartJettyIfNotAlreadyRunning;
+import org.jclouds.virtualbox.functions.admin.StartVBoxIfNotAlreadyRunning;
+import org.jclouds.virtualbox.predicates.SshResponds;
 import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.LockType;
 import org.virtualbox_4_1.MachineState;
 import org.virtualbox_4_1.VirtualBoxManager;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Injector;
 import com.google.inject.Provides;
@@ -57,24 +73,15 @@
 /**
  * @author Mattias Holmqvist, Andrea Turli
  */
+@SuppressWarnings("unchecked")
 public class VirtualBoxComputeServiceContextModule extends
-      ComputeServiceAdapterContextModule<VirtualBoxManager, VirtualBoxManager, IMachine, IMachine, Image, Location> {
+         ComputeServiceAdapterContextModule<Supplier, Supplier, IMachine, IMachine, Image, Location> {
 
    public VirtualBoxComputeServiceContextModule() {
-      super(VirtualBoxManager.class, VirtualBoxManager.class);
+      super(Supplier.class, Supplier.class);
    }
 
-   @Provides
-   @Singleton
-   protected VirtualBoxManager createInstance(@Provider URI endpoint,
-         @Named(Constants.PROPERTY_IDENTITY) String identity, @Named(Constants.PROPERTY_CREDENTIAL) String credential) {
-
-      VirtualBoxManager manager = VirtualBoxManager.createInstance("");
-      manager.connect(endpoint.toASCIIString(), identity, credential);
-      return manager;
-   }
-
-   @SuppressWarnings({ "unchecked", "rawtypes" })
+   @SuppressWarnings( { "unchecked", "rawtypes" })
    @Override
    protected void configure() {
       super.configure();
@@ -92,6 +99,55 @@
       }).to(IMachineToImage.class);
       bind(new TypeLiteral<Supplier<Location>>() {
       }).to(OnlyLocationOrFirstZone.class);
+
+      // for byon
+      bind(new TypeLiteral<Function<URI, InputStream>>() {
+      }).to(SupplyFromProviderURIOrNodesProperty.class);
+
+      bind(new TypeLiteral<Function<IMachine, SshClient>>() {
+      }).to(IMachineToSshClient.class);
+
+      bind(ExecutionType.class).toInstance(ExecutionType.GUI);
+      bind(LockType.class).toInstance(LockType.Write);
+
+   }
+
+   @Provides
+   @Singleton
+   @Preconfiguration
+   protected Supplier<URI> preconfiguration(javax.inject.Provider<StartJettyIfNotAlreadyRunning> lazyGet) {
+      return lazyGet.get();
+   }
+
+   @Provides
+   @Singleton
+   protected Function<Supplier<NodeMetadata>, VirtualBoxManager> provideVBox(Supplier<NodeMetadata> host) {
+      return new Function<Supplier<NodeMetadata>, VirtualBoxManager>(){
+
+         @Override
+         public VirtualBoxManager apply(Supplier<NodeMetadata> arg0) {
+            return VirtualBoxManager.createInstance(arg0.get().getId());
+         }
+         
+      };
+   }
+
+   @Provides
+   @Singleton
+   protected Supplier defaultClient(Supplier<VirtualBoxManager> in) {
+      return in;
+   }
+
+   @Provides
+   @Singleton
+   protected Supplier<VirtualBoxManager> vbox(javax.inject.Provider<StartVBoxIfNotAlreadyRunning> lazyGet) {
+      return lazyGet.get();
+   }
+
+   @Provides
+   @Singleton
+   protected Predicate<SshClient> sshResponds(SshResponds sshResponds, Timeouts timeouts) {
+      return new RetryablePredicate<SshClient>(sshResponds, timeouts.nodeRunning);
    }
 
    @Override
@@ -99,26 +155,37 @@
       return template.osFamily(OsFamily.UBUNTU).osVersionMatches("11.04");
    }
 
+   @Provides
+   @Singleton
+   protected Supplier<NodeMetadata> host(Supplier<LoadingCache<String, Node>> nodes, NodeToNodeMetadata converter)
+            throws ExecutionException {
+      return Suppliers.compose(Functions.compose(converter, new Function<LoadingCache<String, Node>, Node>() {
+
+         @Override
+         public Node apply(LoadingCache<String, Node> arg0) {
+            return arg0.apply("host");
+         }
+      }), nodes);
+   }
+
    @VisibleForTesting
    public static final Map<MachineState, NodeState> machineToNodeState = ImmutableMap
-         .<MachineState, NodeState> builder().put(MachineState.Running, NodeState.RUNNING)
-         .put(MachineState.PoweredOff, NodeState.SUSPENDED).put(MachineState.DeletingSnapshot, NodeState.PENDING)
-         .put(MachineState.DeletingSnapshotOnline, NodeState.PENDING)
-         .put(MachineState.DeletingSnapshotPaused, NodeState.PENDING)
-         .put(MachineState.FaultTolerantSyncing, NodeState.PENDING)
-         .put(MachineState.LiveSnapshotting, NodeState.PENDING)
-         .put(MachineState.SettingUp, NodeState.PENDING)
-         .put(MachineState.Starting, NodeState.PENDING)
-         .put(MachineState.Stopping, NodeState.PENDING)
-         .put(MachineState.Restoring, NodeState.PENDING)
-         // TODO What to map these states to?
-         .put(MachineState.FirstOnline, NodeState.PENDING).put(MachineState.FirstTransient, NodeState.PENDING)
-         .put(MachineState.LastOnline, NodeState.PENDING).put(MachineState.LastTransient, NodeState.PENDING)
-         .put(MachineState.Teleported, NodeState.PENDING).put(MachineState.TeleportingIn, NodeState.PENDING)
-         .put(MachineState.TeleportingPausedVM, NodeState.PENDING)
+            .<MachineState, NodeState> builder().put(MachineState.Running, NodeState.RUNNING).put(
+                     MachineState.PoweredOff, NodeState.SUSPENDED)
+            .put(MachineState.DeletingSnapshot, NodeState.PENDING).put(MachineState.DeletingSnapshotOnline,
+                     NodeState.PENDING).put(MachineState.DeletingSnapshotPaused, NodeState.PENDING).put(
+                     MachineState.FaultTolerantSyncing, NodeState.PENDING).put(MachineState.LiveSnapshotting,
+                     NodeState.PENDING).put(MachineState.SettingUp, NodeState.PENDING).put(MachineState.Starting,
+                     NodeState.PENDING).put(MachineState.Stopping, NodeState.PENDING).put(MachineState.Restoring,
+                     NodeState.PENDING)
+            // TODO What to map these states to?
+            .put(MachineState.FirstOnline, NodeState.PENDING).put(MachineState.FirstTransient, NodeState.PENDING).put(
+                     MachineState.LastOnline, NodeState.PENDING).put(MachineState.LastTransient, NodeState.PENDING)
+            .put(MachineState.Teleported, NodeState.PENDING).put(MachineState.TeleportingIn, NodeState.PENDING).put(
+                     MachineState.TeleportingPausedVM, NodeState.PENDING)
 
-         .put(MachineState.Aborted, NodeState.ERROR).put(MachineState.Stuck, NodeState.ERROR)
+            .put(MachineState.Aborted, NodeState.ERROR).put(MachineState.Stuck, NodeState.ERROR)
 
-         .put(MachineState.Null, NodeState.UNRECOGNIZED).build();
+            .put(MachineState.Null, NodeState.UNRECOGNIZED).build();
 
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java
index 804718e..5f949ec 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java
@@ -27,19 +27,15 @@
  */
 public interface VirtualBoxConstants {
 
-   public static final String VIRTUALBOX_IMAGE_PREFIX = "jclouds-image-";
+   public static final String VIRTUALBOX_IMAGE_PREFIX = "jclouds#image#";
 
-   public static final String VIRTUALBOX_PRESEED_URL = "jclouds.virtualbox.preseedurl";
-
-   public static final String VIRTUALBOX_SNAPSHOT_DESCRIPTION = "jclouds.virtualbox.snapshotDescription";
+   public static final String VIRTUALBOX_PRECONFIGURATION_URL = "jclouds.virtualbox.preconfigurationurl";
 
    public static final String VIRTUALBOX_INSTALLATION_KEY_SEQUENCE = "jclouds.virtualbox.installationkeysequence";
 
-   public static final String VIRTUALBOX_HOSTNAME = "jclouds.virtualbox.hostname";
-
    public static final String VIRTUALBOX_WORKINGDIR = "jclouds.virtualbox.workingdir";
 
-   public static final String VIRTUALBOX_ISOFILE = "jclouds.virtualbox.isofile";
+   public static final String VIRTUALBOX_ISO_URL = "jclouds.virtualbox.isourl";
 
    public static final String VIRTUALBOX_MACHINE_GROUP = "jclouds.virtualbox.machinegroup";
 
@@ -51,12 +47,6 @@
 
    public static final String VIRTUALBOX_HOST_ID = "jclouds.virtualbox.hostid";
 
-   public static final String VIRTUALBOX_DISTRO_ISO_NAME = "jclouds.virtualbox.distroisoname";
-
-   public static final String VIRTUALBOX_JETTY_PORT = "jclouds.virtualbox.jetty.port";
-
-   public static final String VIRTUALBOX_JETTY_BASE_RESOURCE = "jclouds.virtualbox.jetty.baseresource";
-
    public static final String VIRTUALBOX_WEBSERVER_IDENTITY = "jclouds.virtualbox.webserver.identity";
 
    public static final String VIRTUALBOX_WEBSERVER_CREDENTIAL = "jclouds.virtualbox.webserver.credential";
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/GetIPAddressFromMAC.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/GetIPAddressFromMAC.java
index 9e9ece0..19e5bb7 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/GetIPAddressFromMAC.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/GetIPAddressFromMAC.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.domain;
 
 import static com.google.common.base.Preconditions.checkArgument;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/InstallGuestAdditions.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/InstallGuestAdditions.java
index fac25bf..68ccedc 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/InstallGuestAdditions.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/InstallGuestAdditions.java
@@ -1,3 +1,21 @@
+/**
+ * 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.virtualbox.domain;
 
 import static com.google.common.base.Preconditions.checkNotNull;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/IsoImage.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/IsoImage.java
index 529535c..a16a379 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/IsoImage.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/IsoImage.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.domain;
 
 import com.google.common.base.Objects;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NatAdapter.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NatAdapter.java
index 18df552..ac2dfae 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NatAdapter.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NatAdapter.java
@@ -50,11 +50,25 @@
 
       private Set<RedirectRule> redirectRules = Sets.newLinkedHashSet();
 
+      /**
+       * @param host incoming address
+       * @param hostPort
+       * @param guest guest address or empty string for all addresses
+       * @param guestPort
+       * @return
+       */
       public Builder tcpRedirectRule(String host, int hostPort, String guest, int guestPort) {
          redirectRules.add(new RedirectRule(NATProtocol.TCP, host, hostPort, guest, guestPort));
          return this;
       }
-
+      
+      /**
+       * @param host incoming address
+       * @param hostPort
+       * @param guest guest address or empty string for all addresses
+       * @param guestPort
+       * @return
+       */
       public Builder udpRedirectRule(String host, int hostPort, String guest, int guestPort) {
          redirectRules.add(new RedirectRule(NATProtocol.UDP, host, hostPort, guest, guestPort));
          return this;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/RedirectRule.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/RedirectRule.java
index e52b3b9..933aa9c 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/RedirectRule.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/RedirectRule.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.domain;
 
 import com.google.common.base.Objects;
@@ -34,7 +33,15 @@
    private final int hostPort;
    private final String guest;
    private final int guestPort;
-
+   
+   /**
+    * @param protocol
+    * @param host incoming address
+    * @param hostPort
+    * @param guest guest address or empty string for all addresses
+    * @param guestPort
+    * @return
+    */
    public RedirectRule(NATProtocol protocol, String host, int hostPort, String guest, int guestPort) {
       checkNotNull(protocol);
       checkNotNull(host);
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/StorageController.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/StorageController.java
index 215b091..e163c6c 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/StorageController.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/StorageController.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.domain;
 
 import com.google.common.base.Objects;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/VmSpec.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/VmSpec.java
index acbc32a..8f61cdc 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/VmSpec.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/VmSpec.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.domain;
 
 import static com.google.common.base.Preconditions.checkArgument;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java
index 61a40d8..66c95ef 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java
@@ -1,5 +1,4 @@
 /**
- *
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,11 +15,7 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- *
- * 
- * @author Andrea Turli
  */
-
 package org.jclouds.virtualbox.domain;
 
 import static org.jclouds.compute.util.ComputeServiceUtils.parseOsFamilyOrUnrecognized;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AddIDEControllerIfNotExists.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AddIDEControllerIfNotExists.java
index 21a6154..782df20 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AddIDEControllerIfNotExists.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AddIDEControllerIfNotExists.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,14 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import org.jclouds.virtualbox.domain.StorageController;
 import org.virtualbox_4_1.IMachine;
-import org.virtualbox_4_1.StorageBus;
 import org.virtualbox_4_1.VBoxException;
 
 import com.google.common.base.Function;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java
index e7800d2..aa70265 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import static org.virtualbox_4_1.NetworkAdapterType.Am79C973;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachine.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java
similarity index 69%
rename from sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachine.java
rename to sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java
index 5bd188f..8aced46 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachine.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java
@@ -27,18 +27,19 @@
 import org.jclouds.virtualbox.domain.RedirectRule;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.INetworkAdapter;
+import org.virtualbox_4_1.VBoxException;
 
 import com.google.common.base.Function;
 
 /**
  * @author Mattias Holmqvist
  */
-public class AttachNATAdapterToMachine implements Function<IMachine, Void> {
+public class AttachNATAdapterToMachineIfNotAlreadyExists implements Function<IMachine, Void> {
 
    private long adapterSlot;
    private NatAdapter natAdapter;
 
-   public AttachNATAdapterToMachine(long adapterSlot, NatAdapter natAdapter) {
+   public AttachNATAdapterToMachineIfNotAlreadyExists(long adapterSlot, NatAdapter natAdapter) {
       this.adapterSlot = adapterSlot;
       this.natAdapter = natAdapter;
    }
@@ -48,12 +49,15 @@
       INetworkAdapter networkAdapter = machine.getNetworkAdapter(adapterSlot);
       networkAdapter.setAttachmentType(NAT);
       for (RedirectRule rule : natAdapter.getRedirectRules()) {
-         networkAdapter.getNatDriver().addRedirect("guestssh",
-                 rule.getProtocol(),
-                 rule.getHost(),
-                 rule.getHostPort(),
-                 rule.getGuest(),
-                 rule.getGuestPort());
+         try {
+            String ruleName = String.format("%s@%s:%s->%s:%s",rule.getProtocol(), rule.getHost(), rule.getHostPort(), 
+                     rule.getGuest(), rule.getGuestPort());
+            networkAdapter.getNatDriver().addRedirect(ruleName, rule.getProtocol(), rule.getHost(), rule.getHostPort(),
+                     rule.getGuest(), rule.getGuestPort());
+         } catch (VBoxException e) {
+            if (e.getMessage().indexOf("already exists") == -1)
+               throw e;
+         }
       }
       networkAdapter.setEnabled(true);
       machine.saveSettings();
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java
index dd63a31..adae172 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java
@@ -19,29 +19,33 @@
 
 package org.jclouds.virtualbox.functions;
 
-import com.google.common.base.Function;
-import com.google.inject.Inject;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.logging.Logger;
-import org.virtualbox_4_1.*;
+import java.util.ArrayList;
+import java.util.List;
 
 import javax.annotation.Nullable;
 import javax.annotation.Resource;
 import javax.inject.Named;
-import java.util.ArrayList;
-import java.util.List;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.virtualbox.util.MachineUtils.lockMachineAndApply;
-import static org.virtualbox_4_1.LockType.Write;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.virtualbox.config.VirtualBoxConstants;
+import org.jclouds.virtualbox.domain.VmSpec;
+import org.virtualbox_4_1.CloneMode;
+import org.virtualbox_4_1.CloneOptions;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.IProgress;
+import org.virtualbox_4_1.ISnapshot;
+import org.virtualbox_4_1.VBoxException;
+import org.virtualbox_4_1.VirtualBoxManager;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.inject.Inject;
 
 /**
- * CloneAndRegisterMachineFromIMachineIfNotAlreadyExists will take care of the
- * followings: - cloning the master - register the clone machine -
- * ensureBridgedNetworkingIsAppliedToMachine(cloneName, macAddress,
- * hostInterface)
- *
+ * CloneAndRegisterMachineFromIMachineIfNotAlreadyExists will take care of the followings: - cloning
+ * the master - register the clone machine -
+ * 
  * @author Andrea Turli
  */
 public class CloneAndRegisterMachineFromIMachineIfNotAlreadyExists implements Function<IMachine, IMachine> {
@@ -50,46 +54,28 @@
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
-   private VirtualBoxManager manager;
-   private ComputeServiceContext context;
-   private String settingsFile;
-   private String osTypeId;
-   private String vmId;
-   private boolean forceOverwrite;
-   private String cloneName;
-   private String hostId;
-   private String snapshotName;
-   private String snapshotDesc;
-   private String controllerIDE;
+   private final Supplier<VirtualBoxManager> manager;
+   private final String workingDir;
+   private final VmSpec vmSpec;
+   private final boolean isLinkedClone;
 
    @Inject
-   public CloneAndRegisterMachineFromIMachineIfNotAlreadyExists(
-           VirtualBoxManager manager, ComputeServiceContext context,
-           String settingsFile, String osTypeId, String vmId,
-           boolean forceOverwrite, String cloneName, String hostId,
-           String snapshotName, String snapshotDesc, String controllerIDE) {
+   public CloneAndRegisterMachineFromIMachineIfNotAlreadyExists(Supplier<VirtualBoxManager> manager,
+            @Named(VirtualBoxConstants.VIRTUALBOX_WORKINGDIR) String workingDir, VmSpec vmSpec, boolean isLinkedClone) {
       this.manager = manager;
-      this.context = context;
-      this.settingsFile = settingsFile;
-      this.osTypeId = osTypeId;
-      this.vmId = vmId;
-      this.forceOverwrite = forceOverwrite;
-      this.cloneName = cloneName;
-      this.hostId = hostId;
-      this.snapshotName = snapshotName;
-      this.snapshotDesc = snapshotDesc;
-      this.controllerIDE = controllerIDE;
+      this.workingDir = workingDir;
+      this.vmSpec = vmSpec;
+      this.isLinkedClone = isLinkedClone;
    }
 
    @Override
    public IMachine apply(@Nullable IMachine master) {
-      final IVirtualBox vBox = manager.getVBox();
       try {
-         vBox.findMachine(cloneName);
-         throw new IllegalStateException("Machine " + cloneName + " is already registered.");
+         manager.get().getVBox().findMachine(vmSpec.getVmName());
+         throw new IllegalStateException("Machine " + vmSpec.getVmName() + " is already registered.");
       } catch (VBoxException e) {
          if (machineNotFoundException(e))
-            return cloneMachine(cloneName, master);
+            return cloneMachine(vmSpec, master);
          else
             throw e;
       }
@@ -99,13 +85,17 @@
       return e.getMessage().contains("VirtualBox error: Could not find a registered machine named ");
    }
 
-   private IMachine cloneMachine(String cloneName, IMachine master) {
-      IMachine clonedMachine = manager.getVBox().createMachine(settingsFile, cloneName, osTypeId, vmId, forceOverwrite);
+   private IMachine cloneMachine(VmSpec vmSpec, IMachine master) {
+      String settingsFile = manager.get().getVBox().composeMachineFilename(vmSpec.getVmName(), workingDir);
+      IMachine clonedMachine = manager.get().getVBox().createMachine(settingsFile, vmSpec.getVmName(), vmSpec.getOsTypeId(),
+               vmSpec.getVmId(), vmSpec.isForceOverwrite());
       List<CloneOptions> options = new ArrayList<CloneOptions>();
-      options.add(CloneOptions.Link);
+      if (isLinkedClone)
+         options.add(CloneOptions.Link);
 
-      // takeSnapshotIfNotAlreadyExists
-      ISnapshot currentSnapshot = new TakeSnapshotIfNotAlreadyAttached(manager, snapshotName, snapshotDesc).apply(master);
+      // TODO snapshot name
+      ISnapshot currentSnapshot = new TakeSnapshotIfNotAlreadyAttached(manager, "snapshotName", "snapshotDesc")
+               .apply(master);
 
       // clone
       IProgress progress = currentSnapshot.getMachine().cloneTo(clonedMachine, CloneMode.MachineState, options);
@@ -114,33 +104,8 @@
          logger.debug("clone done");
 
       // registering
-      manager.getVBox().registerMachine(clonedMachine);
-
-      // Bridged Network
-      List<String> activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces(context).apply(hostId);
-      checkNotNull(activeBridgedInterfaces);
-      String macAddress = manager.getVBox().getHost().generateMACAddress();
-
-      // TODO this behavior can be improved
-      String bridgedInterface = activeBridgedInterfaces.get(0);
-      long adapterSlot = 0l;
-      ensureBridgedNetworkingIsAppliedToMachine(adapterSlot, cloneName, macAddress, bridgedInterface);
-
-      // detach iso
-      // TODO: also hard-coded values here
-      int controllerPort = 0;
-      int device = 0;
-      ensureMachineHasDistroMediumDetached(cloneName, controllerIDE, controllerPort, device);
-
+      manager.get().getVBox().registerMachine(clonedMachine);
       return clonedMachine;
    }
 
-   private void ensureBridgedNetworkingIsAppliedToMachine(long adapterSlot, String vmName, String macAddress, String hostInterface) {
-      lockMachineAndApply(manager, Write, vmName, new AttachBridgedAdapterToMachine(adapterSlot, macAddress, hostInterface));
-   }
-
-   private void ensureMachineHasDistroMediumDetached(String vmName, String controllerIDE, int controllerPort, int device) {
-      lockMachineAndApply(manager, Write, vmName, new DetachDistroMediumFromMachine(checkNotNull(controllerIDE, "controllerIDE"), controllerPort, device));
-   }
-
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java
index 3ce8b33..9d86fd6 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,191 +16,135 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.inject.Inject;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.domain.ExecResponse;
-import org.jclouds.compute.options.RunScriptOptions;
-import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.logging.Logger;
-import org.jclouds.net.IPSocket;
-import org.jclouds.ssh.SshException;
-import org.jclouds.virtualbox.domain.*;
-import org.jclouds.virtualbox.settings.KeyboardScancodes;
-import org.virtualbox_4_1.*;
+import static com.google.common.base.Preconditions.checkState;
+import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
+import static org.jclouds.virtualbox.util.MachineUtils.applyForMachine;
+import static org.jclouds.virtualbox.util.MachineUtils.lockSessionOnMachineAndApply;
+import static org.virtualbox_4_1.LockType.Shared;
+
+import java.net.URI;
+import java.util.List;
 
 import javax.annotation.Resource;
 import javax.inject.Named;
-import java.io.File;
-import java.util.Map;
-import java.util.Set;
+import javax.inject.Singleton;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot;
-import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript;
-import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
-import static org.jclouds.virtualbox.util.MachineUtils.*;
-import static org.virtualbox_4_1.LockType.Shared;
-import static org.virtualbox_4_1.LockType.Write;
+import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.callables.RunScriptOnNode.Factory;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.config.ValueOfConfigurationKeyOrNull;
+import org.jclouds.logging.Logger;
+import org.jclouds.scriptbuilder.domain.Statements;
+import org.jclouds.ssh.SshClient;
+import org.jclouds.virtualbox.Preconfiguration;
+import org.jclouds.virtualbox.domain.ExecutionType;
+import org.jclouds.virtualbox.domain.VmSpec;
+import org.jclouds.virtualbox.settings.KeyboardScancodes;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.IProgress;
+import org.virtualbox_4_1.ISession;
+import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+
+@Singleton
 public class CreateAndInstallVm implements Function<VmSpec, IMachine> {
 
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
-   private final VirtualBoxManager manager;
-   private String guestId;
-   private final ComputeServiceContext context;
-   private final String hostId;
-   private final Predicate<IPSocket> socketTester;
-   private final String webServerHost;
-   private final int webServerPort;
+   private final Supplier<VirtualBoxManager> manager;
+   private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists;
+   private final ValueOfConfigurationKeyOrNull valueOfConfigurationKeyOrNull;
+
+   private final Supplier<URI> preconfiguration;
+   private final Predicate<SshClient> sshResponds;
    private final ExecutionType executionType;
 
+   private final Factory scriptRunner;
+   private final Supplier<NodeMetadata> host;
+
+   private final Function<IMachine, SshClient> sshClientForIMachine;
+
    @Inject
-   public CreateAndInstallVm(VirtualBoxManager manager, String guestId, ComputeServiceContext context,
-                             String hostId, Predicate<IPSocket> socketTester,
-                             String webServerHost, int webServerPort, ExecutionType executionType) {
+   public CreateAndInstallVm(
+         Supplier<VirtualBoxManager> manager,
+         CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists,
+         ValueOfConfigurationKeyOrNull valueOfConfigurationKeyOrNull,
+         Predicate<SshClient> sshResponds,
+         Function<IMachine, SshClient> sshClientForIMachine,
+         Supplier<NodeMetadata> host, RunScriptOnNode.Factory scriptRunner,
+         @Preconfiguration Supplier<URI> preconfiguration,
+         ExecutionType executionType) {
       this.manager = manager;
-      this.guestId = guestId;
-      this.context = context;
-      this.hostId = hostId;
-      this.socketTester = socketTester;
-      this.webServerHost = webServerHost;
-      this.webServerPort = webServerPort;
+      this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
+      this.valueOfConfigurationKeyOrNull = valueOfConfigurationKeyOrNull;
+      this.sshResponds = sshResponds;
+      this.sshClientForIMachine = sshClientForIMachine;
+      this.scriptRunner = scriptRunner;
+      this.host = host;
+      this.preconfiguration = preconfiguration;
       this.executionType = executionType;
    }
 
    @Override
    public IMachine apply(VmSpec vmSpec) {
-
-      ensureWebServerIsRunning();
-
-      final IMachine vm = new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(manager).apply(vmSpec);
-
       String vmName = vmSpec.getVmName();
 
-      // Change RAM
-      ensureMachineHasMemory(vmName, vmSpec.getMemory());
+      // note this may not be reachable, as this likely uses the 10.2.2 address
+      URI preconfigurationUri = preconfiguration.get();
+      String keySequence = valueOfConfigurationKeyOrNull
+            .apply(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE)
+            .replace("PRECONFIGURATION_URL",
+                  preconfigurationUri.toASCIIString())
+            .replace("HOSTNAME", vmName);
 
-      Set<StorageController> controllers = vmSpec.getControllers();
-      if (controllers.isEmpty()) {
-         throw new IllegalStateException(missingIDEControllersMessage(vmSpec));
-      }
-      StorageController controller = controllers.iterator().next();
-      ensureMachineHasIDEControllerNamed(vmName, controller);
-      setupHardDisksForController(vmName, controller);
-      setupDvdsForController(vmSpec, vmName, controller);
-
-      // NAT
-      Map<Long, NatAdapter> natNetworkAdapters = vmSpec.getNatNetworkAdapters();
-      for (Map.Entry<Long, NatAdapter> natAdapterAndSlot : natNetworkAdapters.entrySet()) {
-         long slotId = natAdapterAndSlot.getKey();
-         NatAdapter natAdapter = natAdapterAndSlot.getValue();
-         ensureNATNetworkingIsAppliedToMachine(vmName, slotId, natAdapter);
-      }
+      final IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists
+            .apply(vmSpec);
 
       // Launch machine and wait for it to come online
       ensureMachineIsLaunched(vmName);
 
-      final String installKeySequence = System.getProperty(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE, defaultInstallSequence(vmName));
-      sendKeyboardSequence(installKeySequence, vmName);
+      sendKeyboardSequence(keySequence, vmName);
 
-      boolean sshDeamonIsRunning = false;
-      while (!sshDeamonIsRunning) {
-         try {
-            if (runScriptOnNode(guestId, "id", wrapInInitScript(false)).getExitCode() == 0) {
-               logger.debug("Got response from ssh daemon.");
-               sshDeamonIsRunning = true;
-            }
-         } catch (SshException e) {
-            logger.debug("No response from ssh daemon...");
-         }
-      }
+      SshClient client = sshClientForIMachine.apply(vm);
 
-      logger.debug("Installation of image complete. Powering down...");
-      lockSessionOnMachineAndApply(manager, Shared, vmName, new Function<ISession, Void>() {
+      logger.debug(">> awaiting installation to finish node(%s)", vmName);
+      checkState(sshResponds.apply(client),
+            "timed out waiting for guest %s to be accessible via ssh", vmName);
 
-         @Override
-         public Void apply(ISession session) {
-            IProgress powerDownProgress = session.getConsole().powerDown();
-            powerDownProgress.waitForCompletion(-1);
-            return null;
-         }
+      logger.debug("<< installation of image complete. Powering down node(%s)",
+            vmName);
+      lockSessionOnMachineAndApply(manager.get(), Shared, vmName,
+            new Function<ISession, Void>() {
 
-      });
+               @Override
+               public Void apply(ISession session) {
+                  IProgress powerDownProgress = session.getConsole()
+                        .powerDown();
+                  powerDownProgress.waitForCompletion(-1);
+                  return null;
+               }
+
+            });
       return vm;
    }
 
-   private void setupDvdsForController(VmSpec vmSpecification, String vmName, StorageController controller) {
-      Set<IsoImage> dvds = controller.getIsoImages();
-      for (IsoImage dvd : dvds) {
-         String dvdSource = dvd.getSourcePath();
-         final IMedium dvdMedium = manager.getVBox().openMedium(dvdSource, DeviceType.DVD,
-                 AccessMode.ReadOnly, vmSpecification.isForceOverwrite());
-         ensureMachineDevicesAttached(vmName, dvdMedium, dvd.getDeviceDetails(), controller.getName());
-      }
-   }
-
-   private void setupHardDisksForController(String vmName, StorageController controller) {
-      Set<HardDisk> hardDisks = controller.getHardDisks();
-      for (HardDisk hardDisk : hardDisks) {
-         String sourcePath = hardDisk.getDiskPath();
-         if (new File(sourcePath).exists()) {
-            boolean deleted = new File(sourcePath).delete();
-            if (!deleted) {
-               logger.error(String.format("File %s could not be deleted.", sourcePath));
-            }
-         }
-         IMedium medium = new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
-         ensureMachineDevicesAttached(vmName, medium, hardDisk.getDeviceDetails(), controller.getName());
-      }
-   }
-
-   private String missingIDEControllersMessage(VmSpec vmSpecification) {
-      return String.format("First controller is not an IDE controller. Please verify that the VM spec is a correct master node: %s", vmSpecification);
-   }
-
-   private void ensureWebServerIsRunning() {
-      final IPSocket webServerSocket = new IPSocket(webServerHost, webServerPort);
-      if (!socketTester.apply(webServerSocket)) {
-         throw new IllegalStateException(String.format("Web server is not running on host %s:%s which is needed to serve preseed.cfg.", webServerHost, webServerPort));
-      }
-   }
-
    private void ensureMachineIsLaunched(String vmName) {
-      applyForMachine(manager, vmName, new LaunchMachineIfNotAlreadyRunning(manager, executionType, ""));
-   }
-
-   private void ensureMachineDevicesAttached(String vmName, IMedium medium, DeviceDetails deviceDetails, String controllerName) {
-      lockMachineAndApply(manager, Write, vmName, new AttachMediumToMachineIfNotAlreadyAttached(deviceDetails, medium, controllerName));
-   }
-
-   private void ensureMachineHasMemory(String vmName, final long memorySize) {
-      lockMachineAndApply(manager, Write, vmName, new ApplyMemoryToMachine(memorySize));
-   }
-
-   private void ensureNATNetworkingIsAppliedToMachine(String vmName, long slotId, NatAdapter natAdapter) {
-      lockMachineAndApply(manager, Write, vmName, new AttachNATAdapterToMachine(slotId, natAdapter));
-   }
-
-   public void ensureMachineHasIDEControllerNamed(String vmName, StorageController storageController) {
-      lockMachineAndApply(manager, Write, checkNotNull(vmName, "vmName"),
-              new AddIDEControllerIfNotExists(checkNotNull(storageController, "storageController")));
-   }
-
-   private String defaultInstallSequence(String vmName) {
-      return "<Esc><Esc><Enter> "
-              + "/install/vmlinuz noapic preseed/url=http://10.0.2.2:" + webServerPort + "/src/test/resources/preseed.cfg "
-              + "debian-installer=en_US auto locale=en_US kbd-chooser/method=us " + "hostname=" + vmName + " "
-              + "fb=false debconf/frontend=noninteractive "
-              + "keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false "
-              + "initrd=/install/initrd.gz -- <Enter>";
+      applyForMachine(manager.get(), vmName,
+            new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType,
+                  ""));
    }
 
    private void sendKeyboardSequence(String keyboardSequence, String vmName) {
@@ -209,16 +153,20 @@
       for (String line : splitSequence) {
          String converted = stringToKeycode(line);
          for (String word : converted.split("  ")) {
-            sb.append("vboxmanage controlvm ").append(vmName).append(" keyboardputscancode ").append(word).append("; ");
+            sb.append("vboxmanage controlvm ").append(vmName)
+                  .append(" keyboardputscancode ").append(word).append("; ");
             runScriptIfWordEndsWith(sb, word, "<Enter>");
             runScriptIfWordEndsWith(sb, word, "<Return>");
          }
       }
    }
 
-   private void runScriptIfWordEndsWith(StringBuilder sb, String word, String key) {
+   private void runScriptIfWordEndsWith(StringBuilder sb, String word,
+         String key) {
       if (word.endsWith(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get(key))) {
-         runScriptOnNode(hostId, sb.toString(), runAsRoot(false).wrapInInitScript(false));
+         scriptRunner
+               .create(host.get(), Statements.exec(sb.toString()),
+                     runAsRoot(false).wrapInInitScript(false)).init().call();
          sb.delete(0, sb.length() - 1);
       }
    }
@@ -228,7 +176,9 @@
       if (s.startsWith("<")) {
          String[] specials = s.split("<");
          for (int i = 1; i < specials.length; i++) {
-            keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<" + specials[i])).append("  ");
+            keycodes.append(
+                  KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<"
+                        + specials[i])).append("  ");
          }
          return keycodes.toString();
       }
@@ -242,13 +192,11 @@
             keycodes.append(" ");
          i++;
       }
-      keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<Spacebar>")).append(" ");
+      keycodes.append(
+            KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<Spacebar>"))
+            .append(" ");
 
       return keycodes.toString();
    }
 
-   protected ExecResponse runScriptOnNode(String nodeId, String command, RunScriptOptions options) {
-      return context.getComputeService().runScriptOnNode(nodeId, command, options);
-   }
-
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java
index c30d392..5947b6e 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java
@@ -19,29 +19,63 @@
 
 package org.jclouds.virtualbox.functions;
 
-import com.google.common.base.Function;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.virtualbox.util.MachineUtils.lockMachineAndApply;
+import static org.virtualbox_4_1.LockType.Write;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.virtualbox.config.VirtualBoxConstants;
+import org.jclouds.virtualbox.domain.DeviceDetails;
+import org.jclouds.virtualbox.domain.HardDisk;
+import org.jclouds.virtualbox.domain.IsoImage;
+import org.jclouds.virtualbox.domain.NatAdapter;
+import org.jclouds.virtualbox.domain.StorageController;
 import org.jclouds.virtualbox.domain.VmSpec;
+import org.virtualbox_4_1.AccessMode;
+import org.virtualbox_4_1.DeviceType;
 import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.IMedium;
 import org.virtualbox_4_1.IVirtualBox;
 import org.virtualbox_4_1.VBoxException;
 import org.virtualbox_4_1.VirtualBoxManager;
 
-import javax.annotation.Nullable;
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
 
 /**
  * @author Mattias Holmqvist
  */
+@Singleton
 public class CreateAndRegisterMachineFromIsoIfNotAlreadyExists implements Function<VmSpec, IMachine> {
 
-   private VirtualBoxManager manager;
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
 
-   public CreateAndRegisterMachineFromIsoIfNotAlreadyExists(VirtualBoxManager manager) {
+   private final Supplier<VirtualBoxManager> manager;
+   private final String workingDir;
+
+   @Inject
+   public CreateAndRegisterMachineFromIsoIfNotAlreadyExists(Supplier<VirtualBoxManager> manager,
+            @Named(VirtualBoxConstants.VIRTUALBOX_WORKINGDIR) String workingDir) {
       this.manager = manager;
+      this.workingDir = workingDir;
    }
 
    @Override
    public IMachine apply(@Nullable VmSpec launchSpecification) {
-      final IVirtualBox vBox = manager.getVBox();
+      final IVirtualBox vBox = manager.get().getVBox();
       String vmName = launchSpecification.getVmName();
       try {
          vBox.findMachine(vmName);
@@ -58,12 +92,89 @@
       return e.getMessage().contains("VirtualBox error: Could not find a registered machine named ");
    }
 
-   private IMachine createMachine(IVirtualBox vBox, VmSpec launchSpecification) {
-      // TODO: add support for settingsfile
-      String settingsFile1 = null;
-      IMachine newMachine = vBox.createMachine(settingsFile1, launchSpecification.getVmName(),
-              launchSpecification.getOsTypeId(), launchSpecification.getVmId(), launchSpecification.isForceOverwrite());
-      manager.getVBox().registerMachine(newMachine);
+   private IMachine createMachine(IVirtualBox vBox, VmSpec vmSpec) {
+      String settingsFile = vBox.composeMachineFilename(vmSpec.getVmName(), workingDir);
+
+      IMachine newMachine = vBox.createMachine(settingsFile, vmSpec.getVmName(), vmSpec.getOsTypeId(),
+               vmSpec.getVmId(), vmSpec.isForceOverwrite());
+      manager.get().getVBox().registerMachine(newMachine);
+      ensureConfiguration(vmSpec);
       return newMachine;
    }
+
+   private void ensureConfiguration(VmSpec vmSpec) {
+      String vmName = vmSpec.getVmName();
+
+      // Change RAM
+      ensureMachineHasMemory(vmName, vmSpec.getMemory());
+
+      Set<StorageController> controllers = vmSpec.getControllers();
+      if (controllers.isEmpty()) {
+         throw new IllegalStateException(missingIDEControllersMessage(vmSpec));
+      }
+      StorageController controller = controllers.iterator().next();
+      ensureMachineHasStorageControllerNamed(vmName, controller);
+      setupHardDisksForController(vmName, controller);
+      setupDvdsForController(vmSpec, vmName, controller);
+
+      // NAT
+      Map<Long, NatAdapter> natNetworkAdapters = vmSpec.getNatNetworkAdapters();
+      for (Map.Entry<Long, NatAdapter> natAdapterAndSlot : natNetworkAdapters.entrySet()) {
+         long slotId = natAdapterAndSlot.getKey();
+         NatAdapter natAdapter = natAdapterAndSlot.getValue();
+         ensureNATNetworkingIsAppliedToMachine(vmName, slotId, natAdapter);
+      }
+   }
+
+   private void setupDvdsForController(VmSpec vmSpecification, String vmName, StorageController controller) {
+      Set<IsoImage> dvds = controller.getIsoImages();
+      for (IsoImage dvd : dvds) {
+         String dvdSource = dvd.getSourcePath();
+         final IMedium dvdMedium = manager.get().getVBox().openMedium(dvdSource, DeviceType.DVD, AccessMode.ReadOnly,
+                  vmSpecification.isForceOverwrite());
+         ensureMachineDevicesAttached(vmName, dvdMedium, dvd.getDeviceDetails(), controller.getName());
+      }
+   }
+
+   private void ensureMachineDevicesAttached(String vmName, IMedium medium, DeviceDetails deviceDetails,
+            String controllerName) {
+      lockMachineAndApply(manager.get(), Write, vmName, new AttachMediumToMachineIfNotAlreadyAttached(deviceDetails, medium,
+               controllerName));
+   }
+
+   private String missingIDEControllersMessage(VmSpec vmSpecification) {
+      return String
+               .format(
+                        "First controller is not an IDE controller. Please verify that the VM spec is a correct master node: %s",
+                        vmSpecification);
+   }
+
+   private void setupHardDisksForController(String vmName, StorageController controller) {
+      Set<HardDisk> hardDisks = controller.getHardDisks();
+      for (HardDisk hardDisk : hardDisks) {
+         String sourcePath = hardDisk.getDiskPath();
+         if (new File(sourcePath).exists()) {
+            boolean deleted = new File(sourcePath).delete();
+            if (!deleted) {
+               logger.error(String.format("File %s could not be deleted.", sourcePath));
+            }
+         }
+         IMedium medium = new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
+         ensureMachineDevicesAttached(vmName, medium, hardDisk.getDeviceDetails(), controller.getName());
+      }
+   }
+
+   private void ensureMachineHasMemory(String vmName, final long memorySize) {
+      lockMachineAndApply(manager.get(), Write, vmName, new ApplyMemoryToMachine(memorySize));
+   }
+
+   private void ensureNATNetworkingIsAppliedToMachine(String vmName, long slotId,
+            NatAdapter natAdapter) {
+      lockMachineAndApply(manager.get(), Write, vmName, new AttachNATAdapterToMachineIfNotAlreadyExists(slotId, natAdapter));
+   }
+
+   public void ensureMachineHasStorageControllerNamed(String vmName, StorageController storageController) {
+      lockMachineAndApply(manager.get(), Write, checkNotNull(vmName, "vmName"), new AddIDEControllerIfNotExists(checkNotNull(
+               storageController, "storageController")));
+   }
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExists.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExists.java
index c8a45ca..d0b3f7c 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExists.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExists.java
@@ -19,34 +19,60 @@
 
 package org.jclouds.virtualbox.functions;
 
-import com.google.common.base.Function;
-import org.jclouds.virtualbox.domain.HardDisk;
-import org.virtualbox_4_1.*;
+import static org.jclouds.virtualbox.util.MachineUtils.lockMachineAndApply;
+import static org.virtualbox_4_1.LockType.Write;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.virtualbox.domain.HardDisk;
+import org.virtualbox_4_1.DeviceType;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.IMedium;
+import org.virtualbox_4_1.IMediumAttachment;
+import org.virtualbox_4_1.IProgress;
+import org.virtualbox_4_1.IVirtualBox;
+import org.virtualbox_4_1.VBoxException;
+import org.virtualbox_4_1.VirtualBoxManager;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
 
 /**
  * @author Mattias Holmqvist
  */
+@Singleton
 public class CreateMediumIfNotAlreadyExists implements Function<HardDisk, IMedium> {
 
-   private final VirtualBoxManager manager;
-   private boolean overwriteIfExists;
+   private final Supplier<VirtualBoxManager> manager;
+   private final boolean overwriteIfExists;
 
-   public CreateMediumIfNotAlreadyExists(VirtualBoxManager manager, boolean overwriteIfExists) {
+   @Inject
+   public CreateMediumIfNotAlreadyExists(Supplier<VirtualBoxManager> manager, boolean overwriteIfExists) {
       this.manager = manager;
       this.overwriteIfExists = overwriteIfExists;
    }
 
+   public static final Pattern ATTACHED_PATTERN = Pattern.compile(".*is still attached.*: ([-0-9a-f]+) .*");
+
    @Override
    public IMedium apply(@Nullable HardDisk hardDisk) {
-      IVirtualBox vBox = manager.getVBox();
+      IVirtualBox vBox = manager.get().getVBox();
       try {
          String diskPath = hardDisk.getDiskPath();
          final IMedium medium = vBox.findMedium(diskPath, DeviceType.HardDisk);
          if (overwriteIfExists) {
-            final IProgress progress = medium.deleteStorage();
-            progress.waitForCompletion(-1);
+            try {
+               deleteMediumAndBlockUntilComplete(medium);
+            } catch (VBoxException e) {
+               onAlreadyAttachedExceptionDetachOrPropagate(vBox, medium, e);
+            }
             return createNewMedium(vBox, hardDisk);
          } else {
             throw new IllegalStateException("Medium for path " + diskPath + " already exists.");
@@ -58,6 +84,30 @@
       }
    }
 
+   private void onAlreadyAttachedExceptionDetachOrPropagate(IVirtualBox vBox, final IMedium medium, VBoxException e) {
+      Matcher matcher = ATTACHED_PATTERN.matcher(e.getMessage());
+      if (matcher.find()) {
+         String machineId = matcher.group(1);
+         IMachine immutableMachine = vBox.findMachine(machineId);
+         IMediumAttachment mediumAttachment = Iterables.find(immutableMachine.getMediumAttachments(),
+                  new Predicate<IMediumAttachment>() {
+                     public boolean apply(IMediumAttachment in) {
+                        return in.getMedium().getId().equals(medium.getId());
+                     }
+                  });
+         lockMachineAndApply(manager.get(), Write, immutableMachine.getName(), new DetachDistroMediumFromMachine(
+                  mediumAttachment.getController(), mediumAttachment.getPort(), mediumAttachment.getDevice()));
+         deleteMediumAndBlockUntilComplete(medium);
+      } else {
+         throw e;
+      }
+   }
+
+   void deleteMediumAndBlockUntilComplete(IMedium medium) {
+      final IProgress progress = medium.deleteStorage();
+      progress.waitForCompletion(-1);
+   }
+
    private IMedium createNewMedium(IVirtualBox vBox, HardDisk hardDisk) {
       IMedium medium = vBox.createHardDisk(hardDisk.getDiskFormat(), hardDisk.getDiskPath());
       createBaseStorage(medium);
@@ -72,7 +122,7 @@
       try {
          long size = 4L * 1024L * 1024L * 1024L - 4L;
          IProgress storageCreation = hardDisk.createBaseStorage(size,
-                 (long) org.virtualbox_4_1.jaxws.MediumVariant.STANDARD.ordinal());
+                  (long) org.virtualbox_4_1.jaxws.MediumVariant.STANDARD.ordinal());
          storageCreation.waitForCompletion(-1);
       } catch (VBoxException e) {
          if (fileNotFoundException(e)) {
@@ -94,7 +144,7 @@
 
    private boolean storageAlreadyExists(VBoxException e) {
       return e.getMessage().contains("VirtualBox error: Storage for the medium ")
-              && e.getMessage().contains("is already created");
+               && e.getMessage().contains("is already created");
    }
 
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/DetachDistroMediumFromMachine.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/DetachDistroMediumFromMachine.java
index 827dff0..7f744e1 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/DetachDistroMediumFromMachine.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/DetachDistroMediumFromMachine.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import javax.annotation.Nullable;
@@ -32,19 +31,20 @@
 public class DetachDistroMediumFromMachine implements Function<IMachine, Void> {
 
    private final String controller;
-   private int controllerPort;
-   private int device;
+   private int port;
+   private int deviceSlot;
 
-   public DetachDistroMediumFromMachine(String controller, int controllerPort, int device) {
+   public DetachDistroMediumFromMachine(String controller, int port, int deviceSlot) {
       this.controller = controller;
-      this.controllerPort = controllerPort;
-      this.device = device;
+      this.port = port;
+      this.deviceSlot = deviceSlot;
    }
 
+   //TODO: should this be a function on HardDisk?
    @Override
    public Void apply(@Nullable IMachine machine) {
       try {
-         machine.detachDevice(controller, controllerPort, device);
+         machine.detachDevice(controller, port, deviceSlot);
          machine.saveSettings();
       } catch (VBoxException e) {
          if (!alreadyDetached(e))
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToHardware.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToHardware.java
index 3bebaa7..3c4ff2d 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToHardware.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToHardware.java
@@ -30,13 +30,14 @@
 import org.virtualbox_4_1.VirtualBoxManager;
 
 import com.google.common.base.Function;
+import com.google.common.base.Supplier;
 
 public class IMachineToHardware implements Function<IMachine, Hardware> {
 
-   private VirtualBoxManager virtualBoxManager;
+   private Supplier<VirtualBoxManager> virtualBoxManager;
 
    @Inject
-   public IMachineToHardware(VirtualBoxManager virtualBoxManager) {
+   public IMachineToHardware(Supplier<VirtualBoxManager> virtualBoxManager) {
       this.virtualBoxManager = virtualBoxManager;
    }
 
@@ -44,7 +45,7 @@
    public Hardware apply(@Nullable IMachine vm) {
       String osTypeId = vm.getOSTypeId();
 
-      IGuestOSType guestOSType = virtualBoxManager.getVBox().getGuestOSType(osTypeId);
+      IGuestOSType guestOSType = virtualBoxManager.get().getVBox().getGuestOSType(osTypeId);
       Boolean is64Bit = guestOSType.getIs64Bit();
       HardwareBuilder hardwareBuilder = new HardwareBuilder();
       hardwareBuilder.ids(vm.getId());
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToImage.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToImage.java
index b3b4f26..acc8ccd 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToImage.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToImage.java
@@ -38,15 +38,16 @@
 import org.virtualbox_4_1.VirtualBoxManager;
 
 import com.google.common.base.Function;
+import com.google.common.base.Supplier;
 
 @Singleton
 public class IMachineToImage implements Function<IMachine, Image> {
 
-   private final VirtualBoxManager virtualboxManager;
+   private final Supplier<VirtualBoxManager> virtualboxManager;
    private final Map<OsFamily, Map<String, String>> osVersionMap;
 
    @Inject
-   public IMachineToImage(VirtualBoxManager virtualboxManager, Map<OsFamily, Map<String, String>> osVersionMap) {
+   public IMachineToImage(Supplier<VirtualBoxManager> virtualboxManager, Map<OsFamily, Map<String, String>> osVersionMap) {
       this.virtualboxManager = checkNotNull(virtualboxManager, "virtualboxManager");
       this.osVersionMap = checkNotNull(osVersionMap, "osVersionMap");
    }
@@ -56,7 +57,7 @@
       if (from == null)
          return null;
 
-      IGuestOSType guestOSType = virtualboxManager.getVBox().getGuestOSType(from.getOSTypeId());
+      IGuestOSType guestOSType = virtualboxManager.get().getVBox().getGuestOSType(from.getOSTypeId());
       OsFamily family = parseOsFamilyOrUnrecognized(guestOSType.getDescription());
       String version = parseVersionOrReturnEmptyString(family, guestOSType.getDescription(), osVersionMap);
       OperatingSystem os = OperatingSystem.builder().description(guestOSType.getDescription()).family(family)
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java
index ee7d74e..1976e64 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java
@@ -33,7 +33,6 @@
 import org.jclouds.compute.domain.NodeState;
 import org.jclouds.compute.domain.Processor;
 import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.domain.Credentials;
 import org.jclouds.domain.LocationBuilder;
 import org.jclouds.domain.LocationScope;
 import org.jclouds.javax.annotation.Nullable;
@@ -43,6 +42,9 @@
 import org.virtualbox_4_1.MachineState;
 
 import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 
 public class IMachineToNodeMetadata implements Function<IMachine, NodeMetadata> {
 
@@ -54,8 +56,7 @@
    public NodeMetadata apply(@Nullable IMachine vm) {
 
       NodeMetadataBuilder nodeMetadataBuilder = new NodeMetadataBuilder();
-      String s = vm.getName();
-      nodeMetadataBuilder.name(s);
+      nodeMetadataBuilder.name(vm.getName()).ids(vm.getName());
 
       // TODO Set up location properly
       LocationBuilder locationBuilder = new LocationBuilder();
@@ -79,7 +80,7 @@
       hardwareBuilder.is64Bit(false);
 
       nodeMetadataBuilder.hostname(vm.getName());
-      nodeMetadataBuilder.loginPort(18083);
+      
 
       MachineState vmState = vm.getState();
       NodeState nodeState = machineToNodeState.get(vmState);
@@ -91,14 +92,21 @@
 
       INetworkAdapter networkAdapter = vm.getNetworkAdapter(0l);
       if (networkAdapter != null) {
-         String bridgedInterface = networkAdapter.getBridgedInterface();
-         System.out.println("Interface: " + bridgedInterface);
+         nodeMetadataBuilder.privateAddresses(ImmutableSet.of(networkAdapter.getNatDriver().getHostIP()));
+         for (String nameProtocolnumberAddressInboudportGuestTargetport : networkAdapter.getNatDriver().getRedirects()){
+            Iterable<String> stuff = Splitter.on(',').split(nameProtocolnumberAddressInboudportGuestTargetport);
+            String protocolNumber  = Iterables.get(stuff, 1);
+            String hostAddress= Iterables.get(stuff, 2);
+            String inboundPort= Iterables.get(stuff, 3);
+            String targetPort= Iterables.get(stuff, 5);
+            if ("1".equals(protocolNumber) && "22".equals(targetPort))
+               nodeMetadataBuilder.privateAddresses(ImmutableSet.of(hostAddress)).loginPort(Integer.parseInt(inboundPort));
+         }
       }
 
       // nodeMetadataBuilder.imageId("");
       // nodeMetadataBuilder.group("");
 
-      nodeMetadataBuilder.id(vm.getId());
       return nodeMetadataBuilder.build();
    }
 
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java
new file mode 100644
index 0000000..97020ab
--- /dev/null
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java
@@ -0,0 +1,66 @@
+/**
+ * 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.virtualbox.functions;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import javax.inject.Singleton;
+
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.net.IPSocket;
+import org.jclouds.ssh.SshClient;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.INetworkAdapter;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+@Singleton
+public class IMachineToSshClient implements Function<IMachine, SshClient> {
+   private final SshClient.Factory sshClientFactory;
+
+   @Inject
+   public IMachineToSshClient(SshClient.Factory sshClientFactory) {
+      this.sshClientFactory = sshClientFactory;
+   }
+
+   @Override
+   public SshClient apply(final IMachine vm) {
+      INetworkAdapter networkAdapter = vm.getNetworkAdapter(0l);
+      SshClient client = null;
+      checkState(networkAdapter != null);
+      for (String nameProtocolnumberAddressInboudportGuestTargetport : networkAdapter.getNatDriver().getRedirects()) {
+         Iterable<String> stuff = Splitter.on(',').split(nameProtocolnumberAddressInboudportGuestTargetport);
+         String protocolNumber = Iterables.get(stuff, 1);
+         String hostAddress = Iterables.get(stuff, 2);
+         String inboundPort = Iterables.get(stuff, 3);
+         String targetPort = Iterables.get(stuff, 5);
+         // TODO: we need a way to align the default login credentials from the iso with the
+         // vmspec
+         if ("1".equals(protocolNumber) && "22".equals(targetPort)) {
+            client = sshClientFactory.create(new IPSocket(hostAddress, Integer.parseInt(inboundPort)),
+                     LoginCredentials.builder().user("toor").password("password").authenticateSudo(true).build());
+         }
+
+      }
+      return client;
+   }
+}
\ No newline at end of file
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToVmSpec.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToVmSpec.java
new file mode 100644
index 0000000..60ea631
--- /dev/null
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToVmSpec.java
@@ -0,0 +1,104 @@
+/**
+ * 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.virtualbox.functions;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.virtualbox.domain.HardDisk;
+import org.jclouds.virtualbox.domain.StorageController;
+import org.jclouds.virtualbox.domain.StorageController.Builder;
+import org.jclouds.virtualbox.domain.VmSpec;
+import org.virtualbox_4_1.CleanupMode;
+import org.virtualbox_4_1.DeviceType;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.IMedium;
+import org.virtualbox_4_1.IMediumAttachment;
+import org.virtualbox_4_1.IStorageController;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+
+/**
+ * Get a VmSpec from an IMachine
+ * 
+ * @author Andrea Turli
+ */
+public class IMachineToVmSpec implements Function<IMachine, VmSpec> {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   @Override
+   public VmSpec apply(@Nullable IMachine machine) {
+      List<StorageController> controllers = buildControllers(machine);
+
+      // TODO some parameters are predefined cause the IMachine doesn't have the
+      // concept i.e.: cleanUpMode
+      org.jclouds.virtualbox.domain.VmSpec.Builder vmSpecBuilder = VmSpec
+            .builder();
+
+      vmSpecBuilder.id(machine.getId()).name(machine.getName())
+            .memoryMB(machine.getMemorySize().intValue())
+            .osTypeId(machine.getOSTypeId()).forceOverwrite(true)
+            .cleanUpMode(CleanupMode.Full);
+
+      for (StorageController storageController : controllers) {
+         vmSpecBuilder.controller(storageController);
+      }
+
+      return vmSpecBuilder.build();
+   }
+
+   private List<StorageController> buildControllers(IMachine machine) {
+
+      List<StorageController> controllers = Lists.newArrayList();
+      for (IStorageController iStorageController : machine
+            .getStorageControllers()) {
+
+         Builder storageControlleBuiler = StorageController.builder();
+         for (IMediumAttachment iMediumAttachment : machine
+               .getMediumAttachmentsOfController(iStorageController.getName())) {
+            IMedium iMedium = iMediumAttachment.getMedium();
+            if (iMedium.getDeviceType().equals(DeviceType.HardDisk)) {
+               storageControlleBuiler.attachHardDisk(HardDisk.builder()
+                     .diskpath(iMedium.getLocation()).autoDelete(true)
+                     .controllerPort(iMediumAttachment.getPort())
+                     .deviceSlot(iMediumAttachment.getDevice().intValue())
+                     .build());
+            } else if (iMedium.getDeviceType().equals(DeviceType.DVD)) {
+               storageControlleBuiler.attachISO(iMediumAttachment.getPort(),
+                     iMediumAttachment.getDevice().intValue(),
+                     iMedium.getLocation());
+            }
+         }
+         controllers.add(storageControlleBuiler
+               .name(iStorageController.getName())
+               .bus(iStorageController.getBus()).build());
+      }
+      return controllers;
+   }
+}
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java
index 71ff12b..e07e5fb 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import static com.google.common.base.Throwables.propagate;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MutableMachine.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MutableMachine.java
new file mode 100644
index 0000000..409bdeb
--- /dev/null
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MutableMachine.java
@@ -0,0 +1,85 @@
+/*
+ * 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.virtualbox.functions;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.ISession;
+import org.virtualbox_4_1.LockType;
+import org.virtualbox_4_1.VBoxException;
+import org.virtualbox_4_1.VirtualBoxManager;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.inject.Inject;
+
+@Singleton
+public class MutableMachine implements Function<String, IMachine> {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final Supplier<VirtualBoxManager> manager;
+   private final LockType lockType;
+
+
+   @Inject
+   public MutableMachine(Supplier<VirtualBoxManager> manager,
+         LockType lockType) {
+      this.manager = manager;
+      this.lockType = lockType;
+   }
+
+	@Override
+	public IMachine apply(String machineId) {
+	 return	lockSessionOnMachineAndReturn(manager.get(), lockType, machineId);
+	}
+
+   /**
+    * Locks the machine and executes the given function using the current session.
+    * Since the machine is locked it is possible to perform some modifications to the IMachine.
+    * <p/>
+    * Unlocks the machine before returning.
+    *
+    * @param manager   the VirtualBoxManager
+    * @param type      the kind of lock to use when initially locking the machine.
+    * @param machineId the id of the machine
+    * @return the result from applying the function to the session.
+    */
+   public static IMachine lockSessionOnMachineAndReturn(VirtualBoxManager manager, LockType type, String machineId) {
+      try {
+         ISession session = manager.getSessionObject();
+         IMachine immutableMachine = manager.getVBox().findMachine(machineId);
+         immutableMachine.lockMachine(session, type);
+         return immutableMachine;
+      } catch (VBoxException e) {
+         throw new RuntimeException(String.format("error locking %s with %s lock: %s", machineId,
+                 type, e.getMessage()), e);
+      }
+   }
+
+
+}
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java
index 5e1e4a2..caa6d97 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfaces.java
@@ -31,9 +31,12 @@
 
 import javax.annotation.Resource;
 
-import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.callables.RunScriptOnNode.Factory;
+import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.logging.Logger;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.Statements;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
@@ -47,27 +50,25 @@
 /**
  * @author Andrea Turli
  */
-public class RetrieveActiveBridgedInterfaces implements Function<String, List<String>> {
+public class RetrieveActiveBridgedInterfaces implements Function<NodeMetadata, List<String>> {
 
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
-   private ComputeServiceContext context;
+   private final Factory runScriptOnNodeFactory;
 
    @Inject
-   public RetrieveActiveBridgedInterfaces(ComputeServiceContext context) {
-      this.context = context;
+   public RetrieveActiveBridgedInterfaces(Factory runScriptOnNodeFactory) {
+      this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory");
    }
 
    @Override
-   public List<String> apply(String hostId) {
+   public List<String> apply(NodeMetadata host) {
       // Bridged Network
-      String command = "vboxmanage list bridgedifs";
-      String bridgedIfBlocks = context
-              .getComputeService()
-              .runScriptOnNode(hostId, command,
-                      runAsRoot(false).wrapInInitScript(false)).getOutput();
+      Statement command = Statements.exec("vboxmanage list bridgedifs");
+      String bridgedIfBlocks = runScriptOnNodeFactory.create(host, command, runAsRoot(false).wrapInInitScript(false))
+               .init().call().getOutput();
 
       List<String> bridgedInterfaces = retrieveBridgedInterfaceNames(bridgedIfBlocks);
       checkNotNull(bridgedInterfaces);
@@ -125,9 +126,8 @@
       @Override
       public boolean apply(String bridgedInterfaceName) {
          try {
-            return (bridgedInterfaceName.startsWith(networkInterface
-                    .getDisplayName()) && networkInterface.isUp() && !networkInterface
-                    .isLoopback());
+            return (bridgedInterfaceName.startsWith(networkInterface.getDisplayName()) && networkInterface.isUp() && !networkInterface
+                     .isLoopback());
          } catch (SocketException e) {
             logger.error(e, "Problem in listing network interfaces.");
             propagate(e);
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java
index d2608e8..0e80ec0 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java
@@ -32,6 +32,7 @@
 import org.virtualbox_4_1.VirtualBoxManager;
 
 import com.google.common.base.Function;
+import com.google.common.base.Supplier;
 import com.google.common.base.Throwables;
 
 /**
@@ -44,12 +45,12 @@
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
    
-   private VirtualBoxManager manager;
+   private Supplier<VirtualBoxManager> manager;
    private String snapshotName;
    private String snapshotDesc;
 
 
-   public TakeSnapshotIfNotAlreadyAttached(VirtualBoxManager manager, String snapshotName, String snapshotDesc) {
+   public TakeSnapshotIfNotAlreadyAttached(Supplier<VirtualBoxManager> manager, String snapshotName, String snapshotDesc) {
       this.manager = manager;
       this.snapshotName = snapshotName;
       this.snapshotDesc = snapshotDesc;
@@ -61,7 +62,7 @@
       ISession session = null;
       if(machine.getCurrentSnapshot() == null ) {
          try {
-            session = manager.openMachineSession(machine);
+            session = manager.get().openMachineSession(machine);
             IProgress progress = session.getConsole().takeSnapshot(snapshotName, snapshotDesc);
             if (progress.getCompleted())
                logger.debug("Snapshot %s (description: %s) taken from %s", snapshotName, snapshotDesc, machine.getName());
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/FileDownloadFromURI.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/FileDownloadFromURI.java
index e4f6953..ccf5cc1 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/FileDownloadFromURI.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/FileDownloadFromURI.java
@@ -35,11 +35,10 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.logging.Logger;
-import org.jclouds.virtualbox.config.VirtualBoxConstants;
+import org.jclouds.rest.HttpClient;
 
 import com.google.common.base.Function;
 
@@ -52,26 +51,22 @@
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
-   private final ComputeServiceContext context;
+   private final HttpClient client;
    private final String workingDir;
-   private final String isoFile;
 
    @Inject
-   public FileDownloadFromURI(final ComputeServiceContext context,
-         @Named(VIRTUALBOX_WORKINGDIR) final String workingDir,
-         @Named(VirtualBoxConstants.VIRTUALBOX_ISOFILE) final String isoFile) {
-      this.context = context;
+   public FileDownloadFromURI(HttpClient client, @Named(VIRTUALBOX_WORKINGDIR) String workingDir) {
+      this.client = client;
       this.workingDir = workingDir;
-      this.isoFile = isoFile;
    }
 
    @Override
    public File apply(@Nullable URI input) {
 
-      final File file = new File(workingDir, isoFile);
+      final File file = new File(workingDir, new File(input.getPath()).getName());
 
       if (!file.exists()) {
-         final InputStream inputStream = context.utils().http().get(input);
+         final InputStream inputStream = client.get(input);
          checkNotNull(inputStream, "%s not found", input);
          try {
             copy(inputStream, new FileOutputStream(file));
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunning.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunning.java
index 9ca1b17..d5359b9 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunning.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunning.java
@@ -21,6 +21,10 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.net.URI;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
 import javax.annotation.Resource;
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -31,51 +35,51 @@
 import org.eclipse.jetty.server.handler.HandlerList;
 import org.eclipse.jetty.server.handler.ResourceHandler;
 import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.logging.Logger;
-import org.jclouds.net.IPSocket;
-import org.jclouds.predicates.InetSocketAddressConnect;
+import org.jclouds.virtualbox.Preconfiguration;
 import org.jclouds.virtualbox.config.VirtualBoxConstants;
 
-import com.google.common.base.Function;
+import com.google.common.base.Supplier;
 import com.google.inject.Singleton;
 
 /**
  * @author Andrea Turli
  */
-public class StartJettyIfNotAlreadyRunning implements Function<String, Server> {
+@Preconfiguration
+@Singleton
+public class StartJettyIfNotAlreadyRunning implements Supplier<URI> {
 
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
+   private final URI preconfigurationUrl;
    private final Server jetty;
-   private final int port;
 
-   public StartJettyIfNotAlreadyRunning(Server jetty, @Named(VirtualBoxConstants.VIRTUALBOX_JETTY_PORT) int port) {
-      this.jetty = checkNotNull(jetty, "jetty");
-      this.port = port;
-   }
-
-   // TODO: getting an instance of the Server object should really be done in
-   // Guice, so inside a *Module class, perhaps as a @Provides method
    @Inject
-   public StartJettyIfNotAlreadyRunning(@Named(VirtualBoxConstants.VIRTUALBOX_JETTY_PORT) int port) {
-      this(ServerJetty.getInstance().getServer(), port);
+   public StartJettyIfNotAlreadyRunning(
+            @Named(VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) {
+      this(new Server(URI.create(preconfigurationUrl).getPort()), preconfigurationUrl);
    }
 
-   @Override
-   public Server apply(@Nullable String baseResource) {
-      if (!jetty.getState().equals(Server.STARTED)
-            // TODO code smell = hard coding addresses or ports!!
-            && !new InetSocketAddressConnect().apply(new IPSocket("localhost", port))) {
+   public StartJettyIfNotAlreadyRunning(Server jetty,
+            @Named(VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) {
+      this.preconfigurationUrl = URI.create(checkNotNull(preconfigurationUrl, "preconfigurationUrl"));
+      this.jetty = jetty;
+   }
+
+   @PostConstruct
+   public void start() {
+
+      if (jetty.getState().equals(Server.STARTED)) {
+         logger.debug("not starting jetty, as existing host is serving %s", preconfigurationUrl);
+      } else {
+         logger.debug(">> starting jetty to serve %s", preconfigurationUrl);
          ResourceHandler resource_handler = new ResourceHandler();
          resource_handler.setDirectoriesListed(true);
          resource_handler.setWelcomeFiles(new String[] { "index.html" });
 
-         resource_handler.setResourceBase(baseResource);
-         logger.info("serving " + resource_handler.getBaseResource());
-
+         resource_handler.setResourceBase("");
          HandlerList handlers = new HandlerList();
          handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() });
          jetty.setHandler(handlers);
@@ -83,34 +87,23 @@
          try {
             jetty.start();
          } catch (Exception e) {
-            logger.error(e, "Server jetty could not be started at this %s", baseResource);
+            logger.error(e, "Server jetty could not be started for %s", preconfigurationUrl);
          }
-         return jetty;
-      } else {
-         logger.debug("Server jetty serving %s already running. Skipping start", baseResource);
-         return jetty;
+         logger.debug("<< serving %s", resource_handler.getBaseResource());
       }
 
    }
 
-   @Singleton
-   private static class ServerJetty {
-      private static ServerJetty instance;
-      private Server server;
-      private String port = System.getProperty(VirtualBoxConstants.VIRTUALBOX_JETTY_PORT, "8080");
-
-      private ServerJetty() {
-         this.server = new Server(Integer.parseInt(port));
+   @PreDestroy()
+   public void stop() {
+      try {
+         jetty.stop();
+      } catch (Exception e) {
       }
+   }
 
-      public static ServerJetty getInstance() {
-         if (instance == null)
-            instance = new ServerJetty();
-         return instance;
-      }
-
-      public Server getServer() {
-         return server;
-      }
+   @Override
+   public URI get() {
+      return preconfigurationUrl;
    }
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java
index 59c4898..91edfcd 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java
@@ -25,68 +25,84 @@
 
 import java.net.URI;
 
+import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
+import javax.inject.Inject;
 import javax.inject.Named;
+import javax.inject.Singleton;
 
-import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.callables.RunScriptOnNode.Factory;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.predicates.RetryIfSocketNotYetOpen;
 import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.domain.Credentials;
+import org.jclouds.location.Provider;
 import org.jclouds.logging.Logger;
 import org.jclouds.net.IPSocket;
+import org.jclouds.rest.annotations.Credential;
+import org.jclouds.rest.annotations.Identity;
+import org.jclouds.scriptbuilder.domain.Statements;
+import org.virtualbox_4_1.SessionState;
 import org.virtualbox_4_1.VirtualBoxManager;
 
 import com.google.common.base.Function;
-import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
 
-public class StartVBoxIfNotAlreadyRunning implements Function<URI, VirtualBoxManager> {
+@Singleton
+public class StartVBoxIfNotAlreadyRunning implements Supplier<VirtualBoxManager> {
 
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
-   private final ComputeService compute;
-   private final VirtualBoxManager manager;
-   private final Predicate<IPSocket> socketTester;
-   private final String hostId;
-   private final Credentials credentials;
+   private final Factory runScriptOnNodeFactory;
+   private final RetryIfSocketNotYetOpen socketTester;
+   private final Supplier<NodeMetadata> host;
+   private final URI provider;
+   private final String identity;
+   private final String credential;
+   private final Function<Supplier<NodeMetadata>, VirtualBoxManager> managerForNode;
+   private transient VirtualBoxManager manager;
 
-   public StartVBoxIfNotAlreadyRunning(ComputeService compute, VirtualBoxManager manager,
-         Predicate<IPSocket> socketTester, String hostId, Credentials credentials) {
-      this.compute = checkNotNull(compute, "compute");
-      this.manager = checkNotNull(manager, "manager");
+   // the functions and suppliers here are to ensure we don't do heavy i/o in injection
+   @Inject
+   public StartVBoxIfNotAlreadyRunning(Function<Supplier<NodeMetadata>, VirtualBoxManager> managerForNode,
+            Factory runScriptOnNodeFactory, RetryIfSocketNotYetOpen socketTester, Supplier<NodeMetadata> host,
+            @Provider URI provider, @Identity String identity, @Credential String credential) {
+      this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory");
       this.socketTester = checkNotNull(socketTester, "socketTester");
-      this.hostId = checkNotNull(hostId, "hostId");
-      this.credentials = checkNotNull(credentials, "credentials");
+      this.host = checkNotNull(host, "host");
+      this.provider = checkNotNull(provider, "endpoint to virtualbox websrvd is needed");
+      this.identity = checkNotNull(identity, "identity");
+      this.credential = checkNotNull(credential, "credential");
+      this.managerForNode = checkNotNull(managerForNode, "managerForNode");
+   }
+
+   @PostConstruct
+   public void start() {
+      if (!socketTester.apply(new IPSocket(provider.getHost(), provider.getPort()))) {
+         logger.debug("disabling password access");
+         runScriptOnNodeFactory.create(host.get(), Statements.exec("VBoxManage setproperty websrvauthlibrary null"),
+                  runAsRoot(false).wrapInInitScript(false)).init().call();
+         logger.debug(">> starting vboxwebsrv");
+         String vboxwebsrv = "vboxwebsrv -t 10000 -v -b";
+         if (host.get().getOperatingSystem() != null
+                  && host.get().getOperatingSystem().getDescription().equals("Mac OS X"))
+            vboxwebsrv = "cd /Applications/VirtualBox.app/Contents/MacOS/ && " + vboxwebsrv;
+
+         runScriptOnNodeFactory.create(host.get(), Statements.exec(vboxwebsrv),
+                  runAsRoot(false).wrapInInitScript(false).blockOnComplete(false).nameTask("vboxwebsrv")).init().call();
+      }
+      manager = managerForNode.apply(host);
+      manager.connect(provider.toASCIIString(), identity, credential);
+      if (logger.isDebugEnabled())
+         if (manager.getSessionObject().getState() == SessionState.Unlocked)
+            logger.warn("manager  is not in unlocked state " + manager);
    }
 
    @Override
-   public VirtualBoxManager apply(URI endpoint) {
-      checkState(compute.getNodeMetadata(hostId) != null, "compute service %s cannot locate node with id %s", compute,
-            hostId);
-      checkNotNull(endpoint, "endpoint to virtualbox websrvd is needed");
-
-      if (socketTester.apply(new IPSocket(endpoint.getHost(), endpoint.getPort()))) {
-         manager.connect(endpoint.toASCIIString(), credentials.identity, credentials.credential);
-         return manager;
-      }
-
-      logger.debug("disabling password access");
-      compute.runScriptOnNode(hostId, "VBoxManage setproperty websrvauthlibrary null", runAsRoot(false)
-            .wrapInInitScript(false));
-      logger.debug("starting vboxwebsrv");
-      String vboxwebsrv = "vboxwebsrv -t 10000 -v -b";
-      if (isOSX(hostId))
-         vboxwebsrv = "cd /Applications/VirtualBox.app/Contents/MacOS/ && " + vboxwebsrv;
-
-      compute.runScriptOnNode(hostId, vboxwebsrv, runAsRoot(false).wrapInInitScript(false).blockOnComplete(false)
-            .nameTask("vboxwebsrv"));
-
-      manager.connect(endpoint.toASCIIString(), credentials.identity, credentials.credential);
+   public VirtualBoxManager get() {
+      checkState(manager != null, "start not called");
       return manager;
    }
 
-   private boolean isOSX(String hostId) {
-      return compute.getNodeMetadata(hostId).getOperatingSystem().getDescription().equals("Mac OS X");
-   }
-
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndDeleteItsMedia.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndDeleteItsMedia.java
index be862bb..ce02937 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndDeleteItsMedia.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndDeleteItsMedia.java
@@ -1,3 +1,21 @@
+/**
+ * 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.
+ */
 /*
 U * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
@@ -20,77 +38,74 @@
 package org.jclouds.virtualbox.functions.admin;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
 
 import java.util.Collections;
 import java.util.List;
 
 import javax.annotation.Resource;
 import javax.inject.Named;
+import javax.inject.Singleton;
 
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.logging.Logger;
 import org.jclouds.virtualbox.domain.ErrorCode;
 import org.jclouds.virtualbox.domain.StorageController;
 import org.jclouds.virtualbox.domain.VmSpec;
+import org.virtualbox_4_1.DeviceType;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.IMedium;
 import org.virtualbox_4_1.IProgress;
 import org.virtualbox_4_1.VBoxException;
-import org.virtualbox_4_1.VirtualBoxManager;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Throwables;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
-public class UnregisterMachineIfExistsAndDeleteItsMedia implements
-      Function<VmSpec, Void> {
+@Singleton
+public class UnregisterMachineIfExistsAndDeleteItsMedia implements Function<IMachine, Void> {
 
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
 
-   private VirtualBoxManager manager;
+   private final VmSpec vmSpec;
 
-   public UnregisterMachineIfExistsAndDeleteItsMedia(VirtualBoxManager manager) {
-      this.manager = manager;
+   public UnregisterMachineIfExistsAndDeleteItsMedia(VmSpec vmSpec) {
+      this.vmSpec = vmSpec;
    }
 
    @Override
-   public Void apply(VmSpec vmSpec) {
+   public Void apply(IMachine machine) {
       List<IMedium> mediaToBeDeleted = Collections.emptyList();
-      IMachine machine = null;
       try {
-         machine = manager.getVBox().findMachine(vmSpec.getVmName());
          mediaToBeDeleted = machine.unregister(vmSpec.getCleanupMode());
       } catch (VBoxException e) {
          ErrorCode errorCode = ErrorCode.valueOf(e);
          switch (errorCode) {
-         case VBOX_E_OBJECT_NOT_FOUND:
-            logger.debug("Machine %s does not exists, cannot unregister",
-                  vmSpec.getVmName());
-            break;
-         default:
-            throw e;
+            case VBOX_E_OBJECT_NOT_FOUND:
+               logger.debug("Machine %s does not exists, cannot unregister", vmSpec.getVmName());
+               break;
+            default:
+               throw e;
          }
       }
 
-      List<IMedium> filteredMediaToBeDeleted = Lists.newArrayList(Iterables
-            .filter(mediaToBeDeleted, new AutoDeleteHardDiskPredicate(vmSpec)));
+      List<IMedium> filteredMediaToBeDeleted = Lists.newArrayList(transform(filter(mediaToBeDeleted,
+               new AutoDeleteHardDiskPredicate(vmSpec)), new DeleteChildrenOfMedium()));
 
-      checkNotNull(filteredMediaToBeDeleted);
       if (!filteredMediaToBeDeleted.isEmpty()) {
          try {
             IProgress deletion = machine.delete(filteredMediaToBeDeleted);
             deletion.waitForCompletion(-1);
-
          } catch (Exception e) {
-            logger.error(e, "Problem in deleting the media attached to %s",
-                  machine.getName());
+            logger.error(e, "Problem in deleting the media attached to %s", machine.getName());
             Throwables.propagate(e);
          }
       }
+      
       return null;
    }
 
@@ -113,4 +128,19 @@
 
    };
 
+   private class DeleteChildrenOfMedium implements Function<IMedium, IMedium> {
+      @Override
+      public IMedium apply(IMedium medium) {
+         checkNotNull(medium.getChildren());
+         if (medium.getDeviceType().equals(DeviceType.HardDisk)) {
+            for (IMedium child : medium.getChildren()) {
+               IProgress deletion = child.deleteStorage();
+               deletion.waitForCompletion(-1);
+            }
+         }
+         return medium;
+      }
+
+   };
+
 }
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/IsLinkedClone.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/IsLinkedClone.java
new file mode 100644
index 0000000..afb95ac
--- /dev/null
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/IsLinkedClone.java
@@ -0,0 +1,92 @@
+/**
+ * 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.virtualbox.predicates;
+
+import javax.annotation.Nullable;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.virtualbox_4_1.DeviceType;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.IMedium;
+import org.virtualbox_4_1.IMediumAttachment;
+import org.virtualbox_4_1.IStorageController;
+import org.virtualbox_4_1.VirtualBoxManager;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+
+/**
+ * 
+ * @author Andrea Turli
+ */
+@Singleton
+public class IsLinkedClone implements Predicate<IMachine> {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final Supplier<VirtualBoxManager> manager;
+
+   @Inject
+   public IsLinkedClone(Supplier<VirtualBoxManager> manager) {
+      this.manager = manager;
+   }
+
+   @Override
+   public boolean apply(@Nullable IMachine machine) {
+
+      for (IStorageController iStorageController : machine
+            .getStorageControllers()) {
+
+         for (IMediumAttachment iMediumAttachment : machine
+               .getMediumAttachmentsOfController(iStorageController.getName())) {
+            IMedium iMedium = iMediumAttachment.getMedium();
+            if (iMedium.getDeviceType().equals(DeviceType.HardDisk)) {
+               if (iMedium.getParent() != null) {
+                  // more than one machine is attached to this hd
+                  for (IMedium child : iMedium.getParent().getChildren()) {
+                     for (String machineId : child.getMachineIds()) {
+                        IMachine iMachine = manager.get().getVBox().findMachine(
+                              machineId);
+                        if (!iMachine.getName().equals(machine.getName())) {
+                           logger.debug("Machine %s is a linked clone",
+                                 machine.getName());
+                           return true;
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+      return false;
+   }
+
+   @Override
+   public String toString() {
+      return "IsLinkedClone()";
+   }
+
+}
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java
index 4be1bf7..ffede70 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.predicates;
 
 import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript;
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java
new file mode 100644
index 0000000..676f42a
--- /dev/null
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java
@@ -0,0 +1,52 @@
+/**
+ * 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.virtualbox.predicates;
+
+import javax.annotation.Resource;
+
+import org.jclouds.logging.Logger;
+import org.jclouds.ssh.SshClient;
+import org.jclouds.ssh.SshException;
+
+import com.google.common.base.Predicate;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class SshResponds implements Predicate<SshClient> {
+   @Resource
+   protected Logger logger = Logger.NULL;
+
+   @Override
+   public boolean apply(SshClient client) {
+
+      try {
+         client.connect();
+         if (client.exec("id").getExitCode() == 0) {
+            return true;
+         }
+      } catch (SshException e) {
+         logger.trace("No response from ssh daemon connecting to %s: %s", client, e.getMessage());
+      } finally {
+         client.disconnect();
+      }
+      return false;
+   }
+}
\ No newline at end of file
diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineUtils.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineUtils.java
index 9a5660d..123646c 100644
--- a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineUtils.java
+++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineUtils.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,21 +16,91 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.util;
 
+import static org.jclouds.scriptbuilder.domain.Statements.call;
+import static org.jclouds.scriptbuilder.domain.Statements.findPid;
+import static org.jclouds.scriptbuilder.domain.Statements.kill;
+import static org.jclouds.scriptbuilder.domain.Statements.newStatementList;
+import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.callables.RunScriptOnNode.Factory;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.scriptbuilder.domain.OsFamily;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.Statements;
+import org.jclouds.util.Throwables2;
+import org.jclouds.virtualbox.functions.MutableMachine;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.ISession;
+import org.virtualbox_4_1.LockType;
+import org.virtualbox_4_1.SessionState;
+import org.virtualbox_4_1.VBoxException;
+import org.virtualbox_4_1.VirtualBoxManager;
+
 import com.google.common.base.Function;
-import org.virtualbox_4_1.*;
+import com.google.common.base.Functions;
+import com.google.common.base.Supplier;
+import com.google.inject.Inject;
 
 /**
  * Utilities for executing functions on a VirtualBox machine.
- *
- * @author Adrian Cole, Mattias Holmqvist
+ * 
+ * @author Adrian Cole, Mattias Holmqvist, Andrea Turli
  */
+
+@Singleton
 public class MachineUtils {
 
-   public static <T> T applyForMachine(VirtualBoxManager manager, final String machineId, final Function<IMachine, T> function) {
-      final IMachine immutableMachine = manager.getVBox().findMachine(machineId);
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final Supplier<VirtualBoxManager> manager;
+   private final LockType lockType;
+   private final Factory scriptRunner;
+   private final Supplier<NodeMetadata> host;
+
+   @Inject
+   public MachineUtils(Supplier<VirtualBoxManager> manager, LockType lockType, RunScriptOnNode.Factory scriptRunner, Supplier<NodeMetadata> host) {
+      super();
+      this.manager = manager;
+      this.lockType = lockType;
+      this.scriptRunner = scriptRunner;
+      this.host = host;
+   }
+
+   public <T> Function<String, T> mutateMachine(String machineId,
+         Function<IMachine, T> function) {
+      try {
+         return Functions.compose(function, new MutableMachine(manager, lockType));
+      } finally {
+         unlockMachine(machineId);
+      }
+   }
+
+   protected void unlockMachine(final String machineId) {
+      IMachine immutableMachine = manager.get().getVBox().findMachine(machineId);
+      if (immutableMachine.getSessionState().equals(SessionState.Locked)) {
+         Statement kill = newStatementList(call("default"),
+               findPid(immutableMachine.getSessionPid().toString()), kill());
+         scriptRunner
+               .create(host.get(), kill,
+                     runAsRoot(false).wrapInInitScript(false)).init().call();
+      }
+   }
+
+   public static <T> T applyForMachine(VirtualBoxManager manager,
+         final String machineId, final Function<IMachine, T> function) {
+      final IMachine immutableMachine = manager.getVBox()
+            .findMachine(machineId);
       return new Function<IMachine, T>() {
          @Override
          public T apply(IMachine machine) {
@@ -45,48 +115,60 @@
    }
 
    /**
-    * Locks the machine and executes the given function using the machine matching the given id.
-    * Since the machine is locked it is possible to perform some modifications to the IMachine.
+    * Locks the machine and executes the given function using the machine
+    * matching the given id. Since the machine is locked it is possible to
+    * perform some modifications to the IMachine.
     * <p/>
     * Unlocks the machine before returning.
-    *
-    * @param manager   the VirtualBoxManager
-    * @param type      the kind of lock to use when initially locking the machine.
-    * @param machineId the id of the machine
-    * @param function  the function to execute
+    * 
+    * @param manager
+    *           the VirtualBoxManager
+    * @param type
+    *           the kind of lock to use when initially locking the machine.
+    * @param machineId
+    *           the id of the machine
+    * @param function
+    *           the function to execute
     * @return the result from applying the function to the machine.
     */
-   public static <T> T lockMachineAndApply(VirtualBoxManager manager, final LockType type, final String machineId,
-                                           final Function<IMachine, T> function) {
-      return lockSessionOnMachineAndApply(manager, type, machineId, new Function<ISession, T>() {
+   public static <T> T lockMachineAndApply(VirtualBoxManager manager,
+         final LockType type, final String machineId,
+         final Function<IMachine, T> function) {
+      return lockSessionOnMachineAndApply(manager, type, machineId,
+            new Function<ISession, T>() {
 
-         @Override
-         public T apply(ISession session) {
-            return function.apply(session.getMachine());
-         }
+               @Override
+               public T apply(ISession session) {
+                  return function.apply(session.getMachine());
+               }
 
-         @Override
-         public String toString() {
-            return function.toString();
-         }
+               @Override
+               public String toString() {
+                  return function.toString();
+               }
 
-      });
+            });
    }
 
    /**
-    * Locks the machine and executes the given function using the current session.
-    * Since the machine is locked it is possible to perform some modifications to the IMachine.
+    * Locks the machine and executes the given function using the current
+    * session. Since the machine is locked it is possible to perform some
+    * modifications to the IMachine.
     * <p/>
     * Unlocks the machine before returning.
-    *
-    * @param manager   the VirtualBoxManager
-    * @param type      the kind of lock to use when initially locking the machine.
-    * @param machineId the id of the machine
-    * @param function  the function to execute
+    * 
+    * @param manager
+    *           the VirtualBoxManager
+    * @param type
+    *           the kind of lock to use when initially locking the machine.
+    * @param machineId
+    *           the id of the machine
+    * @param function
+    *           the function to execute
     * @return the result from applying the function to the session.
     */
-   public static <T> T lockSessionOnMachineAndApply(VirtualBoxManager manager, LockType type, String machineId,
-                                                    Function<ISession, T> function) {
+   public static <T> T lockSessionOnMachineAndApply(VirtualBoxManager manager,
+         LockType type, String machineId, Function<ISession, T> function) {
       try {
          ISession session = manager.getSessionObject();
          IMachine immutableMachine = manager.getVBox().findMachine(machineId);
@@ -97,8 +179,111 @@
             session.unlockMachine();
          }
       } catch (VBoxException e) {
-         throw new RuntimeException(String.format("error applying %s to %s with %s lock: %s", function, machineId,
-                 type, e.getMessage()), e);
+         throw new RuntimeException(String.format(
+               "error applying %s to %s with %s lock: %s", function, machineId,
+               type, e.getMessage()), e);
+      }
+   }
+
+   /**
+    * Locks the machine and executes the given function using the current
+    * session, if the machine is registered. Since the machine is locked it is
+    * possible to perform some modifications to the IMachine.
+    * <p/>
+    * Unlocks the machine before returning.
+    * 
+    * @param manager
+    *           the VirtualBoxManager
+    * @param type
+    *           the kind of lock to use when initially locking the machine.
+    * @param machineId
+    *           the id of the machine
+    * @param function
+    *           the function to execute
+    * @return the result from applying the function to the session.
+    */
+   public static <T> T lockMachineAndApplyOrReturnNullIfNotRegistered(
+         VirtualBoxManager manager, LockType type, String machineId,
+         Function<IMachine, T> function) {
+      try {
+         return lockMachineAndApply(manager, type, machineId, function);
+      } catch (RuntimeException e) {
+         VBoxException vbex = Throwables2.getFirstThrowableOfType(e,
+               VBoxException.class);
+         if (vbex != null
+               && vbex.getMessage().indexOf("not find a registered") == -1)
+            throw e;
+         return null;
+      }
+   }
+
+   /**
+    * Unlocks the machine and executes the given function using the machine
+    * matching the given id. Since the machine is unlocked it is possible to
+    * delete the IMachine.
+    * <p/>
+    * 
+    * <h3>Note!</h3> Currently, this can only unlock the machine, if the lock
+    * was created in the current session.
+    * 
+    * @param manager
+    *           the VirtualBoxManager
+    * @param machineId
+    *           the id of the machine
+    * @param function
+    *           the function to execute
+    * @return the result from applying the function to the machine.
+    */
+   public static <T> T unlockMachineAndApply(VirtualBoxManager manager,
+         final String machineId, final Function<IMachine, T> function) {
+      ISession session = manager.getSessionObject();
+
+      try {
+         IMachine immutableMachine = manager.getVBox().findMachine(machineId);
+         SessionState state = immutableMachine.getSessionState();
+         Statement kill = newStatementList(call("default"),
+               findPid(immutableMachine.getSessionPid().toString()), kill());
+         if (state.equals(SessionState.Locked))
+            // session.unlockMachine();
+            kill.render(OsFamily.UNIX);
+         // TODO: wire this in
+
+         return function.apply(immutableMachine);
+
+      } catch (VBoxException e) {
+         session.unlockMachine();
+         throw new RuntimeException(String.format(
+               "error applying %s to %s: %s", function, machineId,
+               e.getMessage()), e);
+      }
+   }
+
+   /**
+    * Unlocks the machine and executes the given function, if the machine is
+    * registered. Since the machine is unlocked it is possible to delete the
+    * machine.
+    * <p/>
+    * 
+    * @param manager
+    *           the VirtualBoxManager
+    * @param machineId
+    *           the id of the machine
+    * @param function
+    *           the function to execute
+    * @return the result from applying the function to the session.
+    */
+   public static <T> T unlockMachineAndApplyOrReturnNullIfNotRegistered(
+         VirtualBoxManager manager, String machineId,
+         Function<IMachine, T> function) {
+      try {
+         return unlockMachineAndApply(manager, machineId, function);
+      } catch (RuntimeException e) {
+         VBoxException vbex = Throwables2.getFirstThrowableOfType(e,
+               VBoxException.class);
+         if (vbex != null
+               && vbex.getMessage().indexOf("not find a registered") == -1)
+            throw e;
+         return null;
       }
    }
 }
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java
index 9bb431f..99f8307 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java
@@ -19,93 +19,125 @@
 
 package org.jclouds.virtualbox;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.virtualbox.experiment.TestUtils.computeServiceForLocalhostAndGuest;
+import static org.jclouds.virtualbox.util.MachineUtils.unlockMachineAndApplyOrReturnNullIfNotRegistered;
 
 import java.net.URI;
 import java.util.Properties;
 
-import org.eclipse.jetty.server.Server;
+import org.jclouds.Constants;
+import org.jclouds.byon.Node;
+import org.jclouds.byon.config.CacheNodeStoreModule;
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
-import org.jclouds.domain.Credentials;
-import org.jclouds.logging.log4j.config.Log4JLoggingModule;
-import org.jclouds.predicates.InetSocketAddressConnect;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.config.ValueOfConfigurationKeyOrNull;
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.jclouds.virtualbox.config.VirtualBoxConstants;
-import org.jclouds.virtualbox.functions.admin.StartJettyIfNotAlreadyRunning;
-import org.jclouds.virtualbox.functions.admin.StartVBoxIfNotAlreadyRunning;
-import org.testng.annotations.AfterGroups;
+import org.jclouds.virtualbox.domain.VmSpec;
+import org.jclouds.virtualbox.functions.CreateAndInstallVm;
+import org.jclouds.virtualbox.functions.CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
+import org.jclouds.virtualbox.functions.admin.UnregisterMachineIfExistsAndDeleteItsMedia;
+import org.jclouds.virtualbox.util.MachineUtils;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
-import org.virtualbox_4_1.SessionState;
 import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Key;
 import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
 
 /**
  * Tests behavior of {@code VirtualBoxClient}
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live")
-public class BaseVirtualBoxClientLiveTest {
-
-   protected String provider = "virtualbox";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint", "http://localhost:18083/");
-      apiversion = System.getProperty("test." + provider + ".apiversion", "4.1.2r73507");
+@Test(groups = "live", singleThreaded = true, testName = "BaseVirtualBoxClientLiveTest")
+public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest {
+   public BaseVirtualBoxClientLiveTest() {
+      provider = "virtualbox";
    }
 
    protected ComputeServiceContext context;
-   protected VirtualBoxManager manager;
-   protected Server jetty;
+   protected Supplier<VirtualBoxManager> manager;
+   protected Supplier<URI> preconfigurationUri;
+   protected String hostVersion;
+   protected String operatingSystemIso;
+   protected String guestAdditionsIso;
+   protected String adminDisk;
+   protected String workingDir;
+   protected Supplier<NodeMetadata> host;
 
-   @BeforeGroups(groups = { "live" })
-   public void setupClient() {
-      Properties properties = new Properties();
-      properties.setProperty(provider + ".endpoint", endpoint);
-      properties.setProperty(provider + ".apiversion", apiversion);
-      context = new ComputeServiceContextFactory().createContext(provider, identity, credential,
-            ImmutableSet.<Module> of(new Log4JLoggingModule(), new SshjSshClientModule()));
-      jetty = new StartJettyIfNotAlreadyRunning(port).apply(basebaseResource);
-      startVboxIfNotAlreadyRunning();
+   @Override
+   protected void setupCredentials() {
+      // default behavior is to bomb when no user is configured, but we know the default user of
+      // vbox
+      ensureIdentityPropertyIsSpecifiedOrTakeFromDefaults();
+      super.setupCredentials();
    }
 
-   @AfterGroups(groups = "live")
+   protected void ensureIdentityPropertyIsSpecifiedOrTakeFromDefaults() {
+      Properties defaultVBoxProperties = new VirtualBoxPropertiesBuilder().build();
+      if (!System.getProperties().containsKey("test." + provider + ".identity"))
+         System.setProperty("test." + provider + ".identity", defaultVBoxProperties
+                  .getProperty(Constants.PROPERTY_IDENTITY));
+   }
+
+   @BeforeClass(groups = "live")
+   public void setupClient() {
+      setupCredentials();
+      Properties overrides = setupProperties();
+
+      CacheNodeStoreModule hostModule = new CacheNodeStoreModule(ImmutableMap.of("host", Node.builder().id("host")
+               .name("host installing virtualbox").hostname("localhost").osFamily(OsFamily.LINUX.toString())
+               .osDescription(System.getProperty("os.name")).osVersion(System.getProperty("os.version")).group("ssh")
+               .username(System.getProperty("user.name")).credentialUrl(
+                        URI.create("file://" + System.getProperty("user.home") + "/.ssh/id_rsa")).build()));
+
+      context = new ComputeServiceContextFactory().createContext(provider, identity, credential, ImmutableSet
+               .<Module> of(new SLF4JLoggingModule(), new SshjSshClientModule(), hostModule), overrides);
+      Function<String, String> configProperties = context.utils().injector().getInstance(
+               ValueOfConfigurationKeyOrNull.class);
+      imageId = configProperties.apply(ComputeServiceConstants.PROPERTY_IMAGE_ID);
+      workingDir = configProperties.apply(VirtualBoxConstants.VIRTUALBOX_WORKINGDIR);
+      host = context.utils().injector().getInstance(Key.get(new TypeLiteral<Supplier<NodeMetadata>>(){}));
+
+      // this will eagerly startup Jetty, note the impl will shut itself down
+      preconfigurationUri = context.utils().injector().getInstance(Key.get(new TypeLiteral<Supplier<URI>>() {
+      }, Preconfiguration.class));
+      // this will eagerly startup Jetty, note the impl will shut itself down
+      preconfigurationUri.get();
+      
+      manager = context.utils().injector().getInstance(Key.get(new TypeLiteral<Supplier<VirtualBoxManager>>() {
+      }));
+      // this will eagerly startup vbox
+      manager.get();
+
+      hostVersion = Iterables.get(Splitter.on('r').split(context.getProviderSpecificContext().getBuildVersion()), 0);
+      adminDisk = workingDir + "/testadmin.vdi";
+      operatingSystemIso = String.format("%s/%s.iso", workingDir, imageId);
+      guestAdditionsIso = String.format("%s/VBoxGuestAdditions_%s.iso", workingDir, hostVersion);
+   }
+
+   protected void undoVm(VmSpec vmSpecification) {
+      MachineUtils machineUtils = context.utils().injector().getInstance(MachineUtils.class);
+      machineUtils.mutateMachine(vmSpecification.getVmId(), new UnregisterMachineIfExistsAndDeleteItsMedia(vmSpecification));
+   }
+
+   @AfterClass(groups = "live")
    protected void tearDown() throws Exception {
       if (context != null)
          context.close();
-      if (jetty != null)
-         jetty.stop();
-      // TODO: should we stop the vbox manager?
    }
 
-   private String basebaseResource = ".";
-   // TODO: I'd not use 8080, maybe something like 28080
-   // also update pom.xml so that this passes through
-   private int port = Integer.parseInt(System.getProperty(VirtualBoxConstants.VIRTUALBOX_JETTY_PORT, "8080"));
-
-   protected void startVboxIfNotAlreadyRunning() {
-      Credentials localhostCredentials = new Credentials("toor", "password");
-      ComputeServiceContext localHostContext = computeServiceForLocalhostAndGuest("hostId", "localhost", "guestId",
-            "localhost", localhostCredentials);
-
-      manager = new StartVBoxIfNotAlreadyRunning(localHostContext.getComputeService(),
-            VirtualBoxManager.createInstance("hostId"), new InetSocketAddressConnect(), "hostId", localhostCredentials)
-            .apply(URI.create(endpoint));
-
-      assert manager.getSessionObject().getState() == SessionState.Unlocked : "manager needs to be in unlocked state or all tests will fail!!: "
-            + manager;
-   }
 }
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterLiveTest.java
index 0bb1f68..a2f8a2e 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterLiveTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterLiveTest.java
@@ -21,58 +21,33 @@
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
-
-import java.net.URI;
-import java.util.Map;
+import static org.testng.Assert.assertTrue;
 
 import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
-import org.jclouds.compute.config.BaseComputeServiceContextModule;
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.compute.domain.Image;
-import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials;
-import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
 import org.jclouds.domain.Credentials;
-import org.jclouds.json.Json;
-import org.jclouds.json.config.GsonModule;
-import org.jclouds.location.suppliers.JustProvider;
 import org.jclouds.net.IPSocket;
 import org.jclouds.ssh.SshClient;
 import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
-import org.jclouds.virtualbox.functions.IMachineToImage;
-import org.testng.annotations.AfterGroups;
-import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.IMachine;
-import org.virtualbox_4_1.VirtualBoxManager;
 
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.inject.Guice;
 
 @Test(groups = "live", singleThreaded = true, testName = "VirtualBoxComputeServiceAdapterLiveTest")
 public class VirtualBoxComputeServiceAdapterLiveTest extends BaseVirtualBoxClientLiveTest {
 
    private VirtualBoxComputeServiceAdapter adapter;
    private NodeAndInitialCredentials<IMachine> machine;
-   private final Map<OsFamily, Map<String, String>> osVersionMap = new BaseComputeServiceContextModule() {
-   }.provideOsVersionMap(new ComputeServiceConstants.ReferenceData(), Guice.createInjector(new GsonModule())
-         .getInstance(Json.class));
 
-   @BeforeGroups(groups = { "live" })
+   @Override
    public void setupClient() {
       super.setupClient();
-      final VirtualBoxManager manager = getManager();
-      Function<IMachine, Image> iMachineToImage = new IMachineToImage(manager, osVersionMap);
-      adapter = new VirtualBoxComputeServiceAdapter(manager, new JustProvider(provider, URI.create(endpoint),
-            ImmutableSet.<String> of()), iMachineToImage);
-   }
-
-   protected VirtualBoxManager getManager() {
-      return (VirtualBoxManager) context.getProviderSpecificContext().getApi();
+      adapter = context.utils().injector().getInstance(VirtualBoxComputeServiceAdapter.class);
    }
 
    @Test
@@ -120,7 +95,7 @@
    @Test
    public void testListHardwareProfiles() {
       Iterable<IMachine> profiles = adapter.listHardwareProfiles();
-      assertFalse(Iterables.isEmpty(profiles));
+      assertTrue(Iterables.isEmpty(profiles));
       // check state;
    }
 
@@ -132,8 +107,8 @@
       }
       // check state;
    }
-
-   @AfterGroups(groups = "live")
+  
+   @Override
    protected void tearDown() throws Exception {
       if (machine != null)
          adapter.destroyNode(machine.getNodeId() + "");
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterTest.java
index 71d6a4f..b22fb76 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterTest.java
@@ -46,6 +46,7 @@
 import org.virtualbox_4_1.VirtualBoxManager;
 
 import com.google.common.base.Function;
+import com.google.common.base.Suppliers;
 import com.google.common.collect.Iterators;
 import com.google.inject.Guice;
 
@@ -83,8 +84,8 @@
 
       replay(manager, justProvider, vBox, clonedMachine, imageMachine, osType);
 
-      Function<IMachine, Image> iMachineToImage = new IMachineToImage(manager, osMap);
-      VirtualBoxComputeServiceAdapter adapter = new VirtualBoxComputeServiceAdapter(manager, justProvider,
+      Function<IMachine, Image> iMachineToImage = new IMachineToImage(Suppliers.ofInstance(manager), osMap);
+      VirtualBoxComputeServiceAdapter adapter = new VirtualBoxComputeServiceAdapter(Suppliers.ofInstance(manager), justProvider,
             iMachineToImage);
 
       Iterator<Image> iterator = adapter.listImages().iterator();
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java
index 065d5aa..675e2db 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java
@@ -1,3 +1,21 @@
+/**
+ * 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.virtualbox.domain;
 
 import static org.testng.Assert.assertEquals;
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/TestUtils.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/TestUtils.java
deleted file mode 100644
index c7d265b..0000000
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/TestUtils.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * 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.virtualbox.experiment;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Map;
-
-import org.jclouds.byon.Node;
-import org.jclouds.byon.config.CacheNodeStoreModule;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.ComputeServiceContextFactory;
-import org.jclouds.compute.domain.OsFamily;
-import org.jclouds.domain.Credentials;
-import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule;
-import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
-import org.jclouds.sshj.config.SshjSshClientModule;
-
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.inject.Module;
-
-public class TestUtils {
-
-   public static ComputeServiceContext computeServiceForLocalhostAndGuest() throws IOException {
-
-      Node host = Node.builder().id("host").name("host installing virtualbox").hostname("localhost")
-            .osFamily(OsFamily.LINUX.toString()).osDescription(System.getProperty("os.name"))
-            .osVersion(System.getProperty("os.version")).group("ssh").username(System.getProperty("user.name"))
-            .credentialUrl(privateKeyFile()).build();
-      Node guest = Node.builder().id("guest").name("new guest").hostname("localhost").loginPort(2222)
-            .osFamily(OsFamily.UBUNTU.toString()).osDescription("ubuntu/11.04").osVersion(System.getProperty("11.04"))
-            .group("guest").username("toor").sudoPassword("password").credential("password").build();
-
-      final Map<String, Node> nodeMap = ImmutableMap.<String, Node> builder().put("host", host).put("guest", guest)
-            .build();
-      return new ComputeServiceContextFactory().createContext("byon", "foo", "bar", ImmutableSet.<Module> of(
-            new SshjSshClientModule(), new SLF4JLoggingModule(), new BouncyCastleCryptoModule(),
-            new CacheNodeStoreModule(nodeMap)));
-   }
-
-   public static ComputeServiceContext computeServiceForLocalhostAndGuest(String hostId, String hostname,
-         String guestId, String guestHostname, Credentials guestLogin) {
-
-      Node host = Node.builder().id(hostId).name("host installing virtualbox").hostname(hostname)
-            .osFamily(OsFamily.LINUX.toString()).osDescription(System.getProperty("os.name"))
-            .osVersion(System.getProperty("os.version")).group("ssh").username(System.getProperty("user.name"))
-            .credentialUrl(privateKeyFile()).build();
-      Node guest = Node.builder().id(guestId).name("new guest").hostname(guestHostname).loginPort(2222)
-            .osFamily(OsFamily.UBUNTU.toString()).osDescription("ubuntu/11.04").osVersion(System.getProperty("11.04"))
-            .group("jclouds-linux-image").username(guestLogin.identity).sudoPassword(guestLogin.credential)
-            .credential(guestLogin.credential).build();
-
-      final Map<String, Node> nodeMap = ImmutableMap.<String, Node> builder().put(hostId, host).put(guestId, guest)
-            .build();
-      return new ComputeServiceContextFactory().createContext("byon", "foo", "bar", ImmutableSet.<Module> of(
-            new SshjSshClientModule(), new SLF4JLoggingModule(), new BouncyCastleCryptoModule(),
-            new CacheNodeStoreModule(nodeMap)));
-   }
-
-   private static URI privateKeyFile() {
-      try {
-         return new URI("file://" + System.getProperty("user.home") + "/.ssh/id_rsa");
-      } catch (URISyntaxException e) {
-         e.printStackTrace();
-      }
-      return null;
-   }
-
-   public static ComputeServiceContext computeServiceForVirtualBox(LoadingCache<String, Node> cache) {
-      return new ComputeServiceContextFactory().createContext("byon", "foo", "bar", ImmutableSet.<Module> of(
-            new SshjSshClientModule(), new SLF4JLoggingModule(), new BouncyCastleCryptoModule(),
-            new CacheNodeStoreModule(cache)));
-   }
-}
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachineTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachineTest.java
index 777949e..a85789c 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachineTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachineTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import static org.easymock.EasyMock.expect;
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachMediumToMachineIfNotAlreadyAttachedTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachMediumToMachineIfNotAlreadyAttachedTest.java
index 7b1266b..b0843f1 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachMediumToMachineIfNotAlreadyAttachedTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachMediumToMachineIfNotAlreadyAttachedTest.java
@@ -52,7 +52,6 @@
 
       String controllerName = "IDE Controller";
       String diskPath = "/Users/johndoe/jclouds-virtualbox-images/admin.vdi";
-      String diskName = "admin";
       String diskFormat = "vdi";
       int controllerPort = 0;
       int deviceSlot = 1;
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExistsTest.java
similarity index 61%
rename from sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineTest.java
rename to sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExistsTest.java
index 39375e0..a778e7d 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExistsTest.java
@@ -19,6 +19,14 @@
 
 package org.jclouds.virtualbox.functions;
 
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.createMock;
+import static org.easymock.classextension.EasyMock.createNiceMock;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.easymock.classextension.EasyMock.verify;
+import static org.virtualbox_4_1.NATProtocol.TCP;
+import static org.virtualbox_4_1.NetworkAttachmentType.NAT;
+
 import org.jclouds.virtualbox.domain.NatAdapter;
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.IMachine;
@@ -26,16 +34,11 @@
 import org.virtualbox_4_1.INetworkAdapter;
 import org.virtualbox_4_1.VBoxException;
 
-import static org.easymock.EasyMock.expect;
-import static org.easymock.classextension.EasyMock.*;
-import static org.virtualbox_4_1.NATProtocol.TCP;
-import static org.virtualbox_4_1.NetworkAttachmentType.NAT;
-
 /**
  * @author Mattias Holmqvist
  */
-@Test(groups = "unit", testName = "AttachNATAdapterToMachineTest")
-public class AttachNATAdapterToMachineTest {
+@Test(groups = "unit", testName = "AttachNATAdapterToMachineIfNotAlreadyExistsTest")
+public class AttachNATAdapterToMachineIfNotAlreadyExistsTest {
 
    @Test
    public void testApplyNetworkingToNonExistingAdapter() throws Exception {
@@ -47,17 +50,43 @@
       expect(machine.getNetworkAdapter(slotId)).andReturn(networkAdapter);
       networkAdapter.setAttachmentType(NAT);
       expect(networkAdapter.getNatDriver()).andReturn(natEngine);
-      natEngine.addRedirect("guestssh", TCP, "127.0.0.1", 2222, "", 22);
+      
+      natEngine.addRedirect("TCP@127.0.0.1:2222->:22", TCP, "127.0.0.1", 2222, "", 22);
       networkAdapter.setEnabled(true);
       machine.saveSettings();
 
       replay(machine, networkAdapter, natEngine);
       NatAdapter natAdapter = NatAdapter.builder().tcpRedirectRule("127.0.0.1", 2222, "", 22).build();
-      new AttachNATAdapterToMachine(slotId, natAdapter).apply(machine);
+      new AttachNATAdapterToMachineIfNotAlreadyExists(slotId, natAdapter).apply(machine);
 
       verify(machine, networkAdapter, natEngine);
    }
 
+   @Test
+   public void testApplySkipsWhenAlreadyExists() throws Exception {
+      Long slotId = 0l;
+      IMachine machine = createMock(IMachine.class);
+      INetworkAdapter networkAdapter = createMock(INetworkAdapter.class);
+      INATEngine natEngine = createMock(INATEngine.class);
+
+      expect(machine.getNetworkAdapter(slotId)).andReturn(networkAdapter);
+      networkAdapter.setAttachmentType(NAT);
+      expect(networkAdapter.getNatDriver()).andReturn(natEngine);
+      
+      natEngine.addRedirect("TCP@127.0.0.1:2222->:22", TCP, "127.0.0.1", 2222, "", 22);
+      expectLastCall().andThrow(
+               new VBoxException(null, "VirtualBox error: A NAT rule of this name already exists (0x80070057)"));      
+      
+      networkAdapter.setEnabled(true);
+      machine.saveSettings();
+
+      replay(machine, networkAdapter, natEngine);
+      NatAdapter natAdapter = NatAdapter.builder().tcpRedirectRule("127.0.0.1", 2222, "", 22).build();
+      new AttachNATAdapterToMachineIfNotAlreadyExists(slotId, natAdapter).apply(machine);
+
+      verify(machine, networkAdapter, natEngine);
+   }
+   
    @Test(expectedExceptions = VBoxException.class)
    public void testRethrowInvalidAdapterSlotException() throws Exception {
       Long slotId = 30l;
@@ -74,7 +103,7 @@
       replay(machine, networkAdapter, natEngine);
 
       NatAdapter natAdapter = NatAdapter.builder().tcpRedirectRule("127.0.0.1", 2222, "", 22).build();
-      new AttachNATAdapterToMachine(slotId, natAdapter).apply(machine);
+      new AttachNATAdapterToMachineIfNotAlreadyExists(slotId, natAdapter).apply(machine);
 
       verify(machine, networkAdapter, natEngine);
    }
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java
index ad96033..d4558d1 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java
@@ -19,31 +19,22 @@
 
 package org.jclouds.virtualbox.functions;
 
-import static org.jclouds.virtualbox.domain.ExecutionType.HEADLESS;
-import static org.jclouds.virtualbox.experiment.TestUtils.computeServiceForLocalhostAndGuest;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
 import static org.testng.Assert.assertEquals;
-import static org.virtualbox_4_1.NetworkAttachmentType.Bridged;
 
-import java.util.concurrent.TimeUnit;
-
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.domain.Credentials;
-import org.jclouds.net.IPSocket;
-import org.jclouds.predicates.InetSocketAddressConnect;
-import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
 import org.jclouds.virtualbox.domain.HardDisk;
 import org.jclouds.virtualbox.domain.StorageController;
 import org.jclouds.virtualbox.domain.VmSpec;
-import org.jclouds.virtualbox.util.PropertyUtils;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.CleanupMode;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.ISession;
 import org.virtualbox_4_1.StorageBus;
-import org.virtualbox_4_1.VirtualBoxManager;
 
-import com.google.common.base.Predicate;
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * @author Andrea Turli
@@ -51,59 +42,63 @@
 @Test(groups = "live", singleThreaded = true, testName = "CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest")
 public class CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest extends BaseVirtualBoxClientLiveTest {
 
-   private String settingsFile = null;
-   private boolean forceOverwrite = true;
-   private String vmId = "jclouds-image-iso-1";
-   private String osTypeId = "";
-   private String controllerIDE = "IDE Controller";
-   private String guestId = "guest";
-   private String hostId = "host";
-   private String snapshotName = "snap";
-   private String snapshotDesc = "snapDesc";
+   private static final boolean IS_LINKED_CLONE = true;
 
-   private String vmName = "jclouds-image-virtualbox-iso-to-machine-test";
-   private String cloneName = vmName + "_clone";
+   private VmSpec clonedVmSpec;
+   private VmSpec sourceVmSpec;
+
+   private CleanupMode mode = CleanupMode.Full;
+
+   @Override
+   @BeforeClass(groups = "live")
+   public void setupClient() {
+      super.setupClient();
+      String sourceName = VIRTUALBOX_IMAGE_PREFIX 
+               + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, getClass().getSimpleName());
+      String cloneName = VIRTUALBOX_IMAGE_PREFIX 
+            + "Clone#" + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, getClass().getSimpleName()
+            );
+
+      StorageController ideController = StorageController.builder().name("IDE Controller").bus(StorageBus.IDE)
+               .attachISO(0, 0, operatingSystemIso).attachHardDisk(
+                        HardDisk.builder().diskpath(adminDisk).controllerPort(0).deviceSlot(1).autoDelete(true).build()).attachISO(1, 1,
+                        guestAdditionsIso).build();
+
+      sourceVmSpec = VmSpec.builder().id(sourceName).name(sourceName).osTypeId("").memoryMB(512).cleanUpMode(
+               CleanupMode.Full).controller(ideController).forceOverwrite(true).build();
+
+      clonedVmSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(512).cleanUpMode(mode)
+               .forceOverwrite(true).build();
+   }
 
    @Test
    public void testCloneMachineFromAnotherMachine() throws Exception {
-      VirtualBoxManager manager = (VirtualBoxManager) context.getProviderSpecificContext().getApi();
-      ComputeServiceContext localHostContext =
-              computeServiceForLocalhostAndGuest(hostId, "localhost", guestId, "localhost", new Credentials("toor", "password"));
+      try {
+         IMachine source = getSourceNode();
 
-      IMachine master = getMasterNode(manager, localHostContext);
+         if (source.getCurrentSnapshot() != null) {
+            ISession session = manager.get().openMachineSession(source);
+            session.getConsole().deleteSnapshot(source.getCurrentSnapshot().getId());
+            session.unlockMachine();
+         }
 
-      if (master.getCurrentSnapshot() != null) {
-         ISession session = manager.openMachineSession(master);
-         session.getConsole().deleteSnapshot(master.getCurrentSnapshot().getId());
-         session.unlockMachine();
+         IMachine clone = new CloneAndRegisterMachineFromIMachineIfNotAlreadyExists(manager, workingDir, clonedVmSpec,
+                  IS_LINKED_CLONE).apply(source);
+         assertEquals(clone.getName(), clonedVmSpec.getVmName());
+      } finally {
+         for (VmSpec spec : ImmutableSet.of(clonedVmSpec, sourceVmSpec))
+            undoVm(spec);
       }
 
-      IMachine clone = new CloneAndRegisterMachineFromIMachineIfNotAlreadyExists(
-              manager, localHostContext, settingsFile, osTypeId, vmId,
-              forceOverwrite, cloneName, hostId, snapshotName, snapshotDesc,
-              controllerIDE).apply(master);
-      assertEquals(clone.getNetworkAdapter(0L).getAttachmentType(), Bridged);
    }
 
-   private IMachine getMasterNode(VirtualBoxManager manager, ComputeServiceContext localHostContext) {
+   private IMachine getSourceNode() {
       try {
-         Predicate<IPSocket> socketTester = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), 10, 1, TimeUnit.SECONDS);
-         String workingDir = PropertyUtils.getWorkingDirFromProperty();
-         StorageController ideController = StorageController.builder().name(controllerIDE).bus(StorageBus.IDE)
-         .attachISO(0, 0, workingDir + "/ubuntu-11.04-server-i386.iso")
-         .attachHardDisk(HardDisk.builder().diskpath(workingDir + "/testadmin.vdi")
-            .controllerPort(0).deviceSlot(1).build())
-         .attachISO(1, 1, workingDir + "/VBoxGuestAdditions_4.1.2.iso").build();
-         VmSpec vmSpecification = VmSpec.builder().id(vmId).name(vmName).osTypeId(osTypeId)
-         		.memoryMB(512)
-         		.cleanUpMode(CleanupMode.Full)
-               .controller(ideController)
-               .forceOverwrite(true).build();
-         return new CreateAndInstallVm(manager, guestId, localHostContext, hostId, socketTester,
-                 "127.0.0.1", 8080, HEADLESS).apply(vmSpecification);
+         return context.utils().injector().getInstance(CreateAndRegisterMachineFromIsoIfNotAlreadyExists.class).apply(
+                  sourceVmSpec);
       } catch (IllegalStateException e) {
          // already created
-         return manager.getVBox().findMachine(vmName);
+         return manager.get().getVBox().findMachine(sourceVmSpec.getVmId());
       }
    }
 }
\ No newline at end of file
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java
index 3be5c1a..b3249d1 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java
@@ -22,43 +22,34 @@
 import static com.google.common.base.Predicates.equalTo;
 import static com.google.common.collect.Iterables.any;
 import static com.google.common.collect.Iterables.transform;
-import static org.jclouds.virtualbox.domain.ExecutionType.HEADLESS;
-import static org.jclouds.virtualbox.experiment.TestUtils.computeServiceForLocalhostAndGuest;
 import static org.testng.Assert.assertTrue;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
 
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nullable;
 
-import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.config.BaseComputeServiceContextModule;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.domain.Credentials;
 import org.jclouds.json.Json;
 import org.jclouds.json.config.GsonModule;
-import org.jclouds.net.IPSocket;
-import org.jclouds.predicates.InetSocketAddressConnect;
-import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
 import org.jclouds.virtualbox.domain.HardDisk;
 import org.jclouds.virtualbox.domain.NatAdapter;
 import org.jclouds.virtualbox.domain.StorageController;
 import org.jclouds.virtualbox.domain.VmSpec;
-import org.jclouds.virtualbox.functions.admin.UnregisterMachineIfExistsAndDeleteItsMedia;
-import org.jclouds.virtualbox.util.PropertyUtils;
-import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.CleanupMode;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.StorageBus;
-import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.CaseFormat;
 import com.google.common.base.Function;
-import com.google.common.base.Predicate;
 import com.google.inject.Guice;
 
 /**
@@ -71,46 +62,32 @@
    }.provideOsVersionMap(new ComputeServiceConstants.ReferenceData(), Guice.createInjector(new GsonModule())
            .getInstance(Json.class));
 
-   private String vmId = "jclouds-image-iso-1";
-   private String osTypeId = "";
-   private String ideControllerName = "IDE Controller";
-   private String guestId = "guest";
-   private String hostId = "host";
-   private String vmName = "jclouds-image-virtualbox-iso-to-machine-test";
-   private StorageController ideController;
    private VmSpec vmSpecification;
 
-   @BeforeGroups(groups = {"live"})
-   public void setUp() throws Exception {
-      identity = "toor";
-      credential = "password";
-      
-      String workingDir = PropertyUtils.getWorkingDirFromProperty();
-      HardDisk hardDisk = HardDisk.builder().diskpath(workingDir + "/testadmin.vdi").autoDelete(true)
+   @Override
+   @BeforeClass(groups = "live")
+   public void setupClient() {
+      super.setupClient();
+      String vmName = VIRTUALBOX_IMAGE_PREFIX 
+            + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, getClass().getSimpleName());
+
+      HardDisk hardDisk = HardDisk.builder().diskpath(adminDisk).autoDelete(true)
             .controllerPort(0).deviceSlot(1).build();
-      ideController = StorageController.builder().name(ideControllerName).bus(StorageBus.IDE)
-              .attachISO(0, 0, workingDir + "/ubuntu-11.04-server-i386.iso")
+      StorageController ideController = StorageController.builder().name("IDE Controller").bus(StorageBus.IDE)
+              .attachISO(0, 0, operatingSystemIso)
               .attachHardDisk(hardDisk)
-              .attachISO(1, 1, workingDir + "/VBoxGuestAdditions_4.1.2.iso").build();
-      vmSpecification = VmSpec.builder().id(vmId).name(vmName).memoryMB(512).osTypeId(osTypeId)
+              .attachISO(1, 1, guestAdditionsIso).build();
+      vmSpecification = VmSpec.builder().id("jclouds-image-iso-1").name(vmName).memoryMB(512).osTypeId("")
               .controller(ideController)
               .forceOverwrite(true)
               .cleanUpMode(CleanupMode.Full)
               .natNetworkAdapter(0, NatAdapter.builder().tcpRedirectRule("127.0.0.1", 2222, "", 22).build()).build();
-      
-      new UnregisterMachineIfExistsAndDeleteItsMedia(manager).apply(vmSpecification);
+      undoVm(vmSpecification);
    }
 
    public void testCreateImageMachineFromIso() throws Exception {
 
-      VirtualBoxManager manager = (VirtualBoxManager) context.getProviderSpecificContext().getApi();
-      ComputeServiceContext localHostContext = computeServiceForLocalhostAndGuest(hostId, "localhost", guestId,
-              "localhost", new Credentials("toor", "password"));
-      Predicate<IPSocket> socketTester = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), 10, 1, TimeUnit.SECONDS);
-
-      IMachine imageMachine = new CreateAndInstallVm(manager, guestId, localHostContext, hostId,
-              socketTester, "127.0.0.1", 8080, HEADLESS)
-              .apply(vmSpecification);
+      IMachine imageMachine = context.utils().injector().getInstance(CreateAndInstallVm.class).apply(vmSpecification);
 
       IMachineToImage iMachineToImage = new IMachineToImage(manager, map);
       Image newImage = iMachineToImage.apply(imageMachine);
@@ -130,5 +107,10 @@
          }
       };
    }
-
+   @Override
+   @AfterClass(groups = "live")
+   protected void tearDown() throws Exception {
+      undoVm(vmSpecification);
+      super.tearDown();
+   }
 }
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java
index b842ef7..9f8664d 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import static org.testng.Assert.assertEquals;
@@ -27,9 +26,6 @@
 import org.jclouds.virtualbox.domain.HardDisk;
 import org.jclouds.virtualbox.domain.StorageController;
 import org.jclouds.virtualbox.domain.VmSpec;
-import org.jclouds.virtualbox.functions.admin.UnregisterMachineIfExistsAndDeleteItsMedia;
-import org.jclouds.virtualbox.util.PropertyUtils;
-import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.CleanupMode;
 import org.virtualbox_4_1.IMachine;
@@ -39,52 +35,60 @@
 /**
  * @author Mattias Holmqvist
  */
+@Test(groups = "live", singleThreaded = true, testName = "CreateAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest")
 public class CreateAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest extends BaseVirtualBoxClientLiveTest {
 
    private String ideControllerName;
    private CleanupMode mode;
-	private StorageController ideController;
-	
-	@BeforeMethod
-	public void setUp() {
-		ideControllerName = "IDE Controller";
-		mode = CleanupMode.Full;
-      String workingDir = PropertyUtils.getWorkingDirFromProperty();
-      ideController = StorageController.builder().name(ideControllerName).bus(StorageBus.IDE)
-              .attachISO(0, 0, workingDir + "/ubuntu-11.04-server-i386.iso")
-              .attachHardDisk(HardDisk.builder().diskpath(workingDir + "/testadmin.vdi")
-                    .controllerPort(0).deviceSlot(1).build())
-              .attachISO(1, 1, workingDir + "/VBoxGuestAdditions_4.1.2.iso").build();
-      }
+   private StorageController ideController;
 
-	@Test
+   @Override
+   public void setupClient() {
+      super.setupClient();
+      ideControllerName = "IDE Controller";
+      mode = CleanupMode.Full;
+      ideController = StorageController.builder().name(ideControllerName).bus(StorageBus.IDE).attachISO(0, 0,
+               operatingSystemIso).attachHardDisk(
+               HardDisk.builder().diskpath(adminDisk).controllerPort(0).deviceSlot(1).build()).attachISO(1, 1,
+               guestAdditionsIso).build();
+   }
+
+   @Test
    public void testCreateNewMachine() throws Exception {
       String vmName = "jclouds-test-create-1-node";
-      VmSpec launchSpecification = VmSpec.builder().id(vmName).name(vmName).memoryMB(512).controller(ideController).cleanUpMode(mode)
-            .osTypeId("Debian").forceOverwrite(true).build();
-      new UnregisterMachineIfExistsAndDeleteItsMedia(manager).apply(launchSpecification);
-      IMachine debianNode = new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(manager).apply(launchSpecification);
-      IMachine machine = manager.getVBox().findMachine(vmName);
-      assertEquals(debianNode.getName(), machine.getName());
-      new UnregisterMachineIfExistsAndDeleteItsMedia(manager).apply(launchSpecification);
+      VmSpec launchSpecification = VmSpec.builder().id(vmName).name(vmName).memoryMB(512).controller(ideController)
+               .cleanUpMode(mode).osTypeId("Debian").forceOverwrite(true).build();
+      undoVm(launchSpecification);
+      try {
+         IMachine debianNode = context.utils().injector().getInstance(
+                  CreateAndRegisterMachineFromIsoIfNotAlreadyExists.class).apply(launchSpecification);
+         IMachine machine = manager.get().getVBox().findMachine(vmName);
+         assertEquals(debianNode.getName(), machine.getName());
+      } finally {
+         undoVm(launchSpecification);
+      }
    }
 
    @Test
    public void testCreateNewMachineWithBadOsType() throws Exception {
       String vmName = "jclouds-test-create-2-node";
-      VmSpec launchSpecification = VmSpec.builder().id(vmName).name(vmName).memoryMB(512).controller(ideController).cleanUpMode(mode)
-            .osTypeId("SomeWeirdUnknownOs").forceOverwrite(true).build();
-      new UnregisterMachineIfExistsAndDeleteItsMedia(manager).apply(launchSpecification);
+      VmSpec launchSpecification = VmSpec.builder().id(vmName).name(vmName).memoryMB(512).controller(ideController)
+               .cleanUpMode(mode).osTypeId("SomeWeirdUnknownOs").forceOverwrite(true).build();
 
+      undoVm(launchSpecification);
       try {
-         new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(manager).apply(launchSpecification);
+         context.utils().injector().getInstance(CreateAndRegisterMachineFromIsoIfNotAlreadyExists.class).apply(
+                  launchSpecification);
          fail();
       } catch (VBoxException e) {
          ErrorCode errorCode = ErrorCode.valueOf(e);
          // According to the documentation VBOX_E_OBJECT_NOT_FOUND
          // if osTypeId is not found.
          assertEquals(errorCode, ErrorCode.VBOX_E_OBJECT_NOT_FOUND);
+      } finally {
+         undoVm(launchSpecification);
       }
+
    }
 
 }
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExistsTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExistsTest.java
index c2b7d2f..d285e3f 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExistsTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExistsTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import static org.easymock.EasyMock.anyBoolean;
@@ -33,28 +32,35 @@
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.CleanupMode;
 import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.ISession;
 import org.virtualbox_4_1.IVirtualBox;
+import org.virtualbox_4_1.LockType;
 import org.virtualbox_4_1.VBoxException;
 import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Suppliers;
+
 /**
  * @author Mattias Holmqvist
  */
 @Test(groups = "unit", testName = "CreateAndRegisterMachineFromIsoIfNotAlreadyExistsTest")
 public class CreateAndRegisterMachineFromIsoIfNotAlreadyExistsTest {
 
-   @Test
-   public void testCreateIfNotAlreadyExists() throws Exception {
+   @Test(enabled = false)
+   public void testCreateAndSetMemoryWhenNotAlreadyExists() throws Exception {
 
-      VirtualBoxManager manager = createNiceMock(VirtualBoxManager.class);
+      VirtualBoxManager manager = createMock(VirtualBoxManager.class);
       IVirtualBox vBox = createMock(IVirtualBox.class);
       String vmName = "jclouds-image-my-ubuntu-image";
 
-      VmSpec launchSpecification = VmSpec.builder().id(vmName).name(vmName).osTypeId("").memoryMB(1024).cleanUpMode(CleanupMode.Full).build();
+      VmSpec launchSpecification = VmSpec.builder().id(vmName).name(vmName).osTypeId("").memoryMB(1024).cleanUpMode(
+               CleanupMode.Full).build();
 
       IMachine createdMachine = createMock(IMachine.class);
+      ISession session = createMock(ISession.class);
 
       expect(manager.getVBox()).andReturn(vBox).anyTimes();
+      expect(vBox.composeMachineFilename(vmName, "/tmp/workingDir")).andReturn("settingsFile");
 
       StringBuilder errorMessageBuilder = new StringBuilder();
       errorMessageBuilder.append("VirtualBox error: Could not find a registered machine named ");
@@ -62,19 +68,28 @@
       String errorMessage = errorMessageBuilder.toString();
       VBoxException vBoxException = new VBoxException(createNiceMock(Throwable.class), errorMessage);
 
-      vBox.findMachine(vmName);
-      expectLastCall().andThrow(vBoxException);
+      expect(vBox.findMachine(vmName)).andThrow(vBoxException);
 
       expect(vBox.createMachine(anyString(), eq(vmName), anyString(), anyString(), anyBoolean())).andReturn(
-              createdMachine).anyTimes();
-
+               createdMachine).anyTimes();
       vBox.registerMachine(createdMachine);
+      
+      expect(vBox.findMachine(vmName)).andReturn(createdMachine).anyTimes();
+      expect(manager.getSessionObject()).andReturn(session);
+      expect(session.getMachine()).andReturn(createdMachine);
+      createdMachine.lockMachine(session, LockType.Write);
+      createdMachine.setMemorySize(1024l);
+      createdMachine.saveSettings();
+      session.unlockMachine();
+      
+      
+      //TODO: this mock test is not finished.
+      
+      replay(manager, createdMachine, vBox, session);
 
-      replay(manager, vBox);
+      new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(Suppliers.ofInstance(manager), "/tmp/workingDir").apply(launchSpecification);
 
-      new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(manager).apply(launchSpecification);
-
-      verify(manager, vBox);
+      verify(manager, createdMachine, vBox, session);
    }
 
    @Test(expectedExceptions = IllegalStateException.class)
@@ -91,8 +106,9 @@
 
       replay(manager, vBox);
 
-      VmSpec launchSpecification = VmSpec.builder().id("").name(vmName).osTypeId("").memoryMB(1024).cleanUpMode(CleanupMode.Full).build();
-      new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(manager).apply(launchSpecification);
+      VmSpec launchSpecification = VmSpec.builder().id("").name(vmName).osTypeId("").memoryMB(1024).cleanUpMode(
+               CleanupMode.Full).build();
+      new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(Suppliers.ofInstance(manager), "/tmp/workingDir").apply(launchSpecification);
    }
 
    @Test(expectedExceptions = VBoxException.class)
@@ -112,12 +128,13 @@
 
       replay(manager, vBox);
 
-      VmSpec launchSpecification = VmSpec.builder().id("").name(vmName).osTypeId("").cleanUpMode(CleanupMode.Full).memoryMB(1024).build();
-      new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(manager).apply(launchSpecification);
+      VmSpec launchSpecification = VmSpec.builder().id("").name(vmName).osTypeId("").cleanUpMode(CleanupMode.Full)
+               .memoryMB(1024).build();
+      new CreateAndRegisterMachineFromIsoIfNotAlreadyExists(Suppliers.ofInstance(manager), "/tmp/workingDir").apply(launchSpecification);
 
    }
 
    private String anyString() {
-      return EasyMock.<String>anyObject();
+      return EasyMock.<String> anyObject();
    }
 }
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExistsLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExistsLiveTest.java
index c38c5d7..0ced37c 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExistsLiveTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExistsLiveTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import static org.testng.Assert.assertEquals;
@@ -30,6 +29,8 @@
 import org.jclouds.virtualbox.domain.HardDisk;
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.DeviceType;
+import org.virtualbox_4_1.IMedium;
+import org.virtualbox_4_1.IProgress;
 import org.virtualbox_4_1.VBoxException;
 
 /**
@@ -41,9 +42,13 @@
    public void testCreateMedium() throws Exception {
       String path = System.getProperty("user.home") + "/jclouds-virtualbox-test/test-medium-1.vdi";
       HardDisk hardDisk = HardDisk.builder().diskpath(path).controllerPort(0).deviceSlot(0).build();
-      new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
-      manager.getVBox().findMedium(path, DeviceType.HardDisk);
-      assertFileCanBeDeleted(path);
+      IMedium iMedium = new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
+      manager.get().getVBox().findMedium(path, DeviceType.HardDisk);
+      try {
+         assertFileCanBeDeleted(path);
+      } finally {
+         deleteMediumAndBlockUntilComplete(iMedium);
+      }
    }
 
    @Test
@@ -63,10 +68,15 @@
    public void testCreateSameMediumTwiceWhenUsingOverwrite() throws Exception {
       String path = System.getProperty("user.home") + "/jclouds-virtualbox-test/test-medium-3.vdi";
       HardDisk hardDisk = HardDisk.builder().diskpath(path).controllerPort(0).deviceSlot(0).build();
-      new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
-      new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
-      manager.getVBox().findMedium(path, DeviceType.HardDisk);
-      assertFileCanBeDeleted(path);
+      IMedium iMedium = new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
+      iMedium = new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
+      manager.get().getVBox().findMedium(path, DeviceType.HardDisk);
+      try {
+         assertFileCanBeDeleted(path);
+      } finally {
+         deleteMediumAndBlockUntilComplete(iMedium);
+      }
+
    }
 
    private void assertFileCanBeDeleted(String path) {
@@ -74,4 +84,10 @@
       boolean mediumDeleted = file.delete();
       assertTrue(mediumDeleted);
    }
+
+   void deleteMediumAndBlockUntilComplete(IMedium medium) {
+      final IProgress progress = medium.deleteStorage();
+      progress.waitForCompletion(-1);
+   }
+
 }
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExistsTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExistsTest.java
index 5952faf..685cd25 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExistsTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExistsTest.java
@@ -34,11 +34,17 @@
 import org.virtualbox_4_1.DeviceType;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.IMedium;
+import org.virtualbox_4_1.IMediumAttachment;
 import org.virtualbox_4_1.IProgress;
+import org.virtualbox_4_1.ISession;
 import org.virtualbox_4_1.IVirtualBox;
+import org.virtualbox_4_1.LockType;
 import org.virtualbox_4_1.VBoxException;
 import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+
 /**
  * @author Mattias Holmqvist
  */
@@ -46,14 +52,11 @@
 
    private String adminDiskPath;
    private String diskFormat;
-   private String diskName;
-
 
    @BeforeMethod
    public void setUp() throws Exception {
       adminDiskPath = "/Users/johndoe/jclouds-virtualbox-images/admin.vdi";
       diskFormat = "vdi";
-      diskName = "diskName";
    }
 
    @Test
@@ -82,7 +85,7 @@
 
       replay(manager, machine, vBox, medium);
 
-      new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
+      new CreateMediumIfNotAlreadyExists(Suppliers.ofInstance(manager), true).apply(hardDisk);
 
       verify(machine, vBox);
 
@@ -108,12 +111,79 @@
 
       replay(manager, machine, vBox, medium, newHardDisk, progress);
 
-      IMedium newDisk = new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
+      IMedium newDisk =  new CreateMediumIfNotAlreadyExists(Suppliers.ofInstance(manager), true).apply(hardDisk);
 
       verify(machine, vBox, medium);
       assertNotSame(newDisk, medium);
    }
 
+   @Test
+   public void testDeleteAndCreateNewStorageWhenMediumExistsAndUsingOverwriteAndStillAttachedDetachesOldThing()
+            throws Exception {
+      HardDisk hardDisk = createTestHardDisk();
+
+      VirtualBoxManager manager = createNiceMock(VirtualBoxManager.class);
+      IMachine machine = createMock(IMachine.class);
+      IVirtualBox vBox = createMock(IVirtualBox.class);
+      IMedium medium = createMock(IMedium.class);
+
+      IMedium newHardDisk = createMock(IMedium.class);
+      IProgress progress = createNiceMock(IProgress.class);
+
+      expect(manager.getVBox()).andReturn(vBox).anyTimes();
+      expect(vBox.findMedium(adminDiskPath, DeviceType.HardDisk)).andReturn(medium);
+
+      String oldMachineId = "a1e03931-29f3-4370-ada3-9547b1009212";
+      String oldMachineName = "oldMachine";
+      IMachine oldMachine = createMock(IMachine.class);
+      IMediumAttachment oldAttachment = createMock(IMediumAttachment.class);
+      String oldAttachmentController = "oldAttachmentController";
+      int oldAttachmentDevice = 1;
+      int oldAttachmentPort = 2;
+      IMedium oldMedium = createMock(IMedium.class);
+      String oldMediumId = "oldMediumId";
+      ISession detachSession = createNiceMock(ISession.class);
+
+      StringBuilder errorBuilder = new StringBuilder();
+      errorBuilder.append("org.virtualbox_4_1.VBoxException: VirtualBox error: ");
+      errorBuilder.append("Cannot delete storage: medium '/Users/adriancole/jclouds-virtualbox-test/testadmin.vdi ");
+      errorBuilder.append("is still attached to the following 1 virtual machine(s): ");
+      errorBuilder.append(oldMachineId + " (0x80BB000C)");
+      String errorMessage = errorBuilder.toString();
+
+      VBoxException stillAttached = new VBoxException(createNiceMock(Throwable.class), errorMessage);
+      expect(medium.deleteStorage()).andThrow(stillAttached);
+
+      expect(vBox.findMachine(oldMachineId)).andReturn(oldMachine);
+      expect(oldMachine.getMediumAttachments()).andReturn(ImmutableList.of(oldAttachment));
+      expect(oldAttachment.getMedium()).andReturn(oldMedium);
+      expect(oldMedium.getId()).andReturn(oldMediumId);
+      // in this case, they are the same medium, so safe to detach
+      expect(medium.getId()).andReturn(oldMediumId);
+      expect(oldMachine.getName()).andReturn(oldMachineName);
+      expect(oldAttachment.getController()).andReturn(oldAttachmentController);
+      expect(oldAttachment.getDevice()).andReturn(oldAttachmentDevice);
+      expect(oldAttachment.getPort()).andReturn(oldAttachmentPort);
+      // TODO: is this ok that we searched by ID last time?
+      expect(vBox.findMachine(oldMachineName)).andReturn(oldMachine);
+      expect(manager.getSessionObject()).andReturn(detachSession);
+      oldMachine.lockMachine(detachSession, LockType.Write);
+      expect(detachSession.getMachine()).andReturn(oldMachine);
+      oldMachine.detachDevice(oldAttachmentController, oldAttachmentPort, oldAttachmentDevice);
+      oldMachine.saveSettings();
+
+      expect(medium.deleteStorage()).andReturn(progress);
+      expect(vBox.createHardDisk(diskFormat, adminDiskPath)).andReturn(newHardDisk);
+      expect(newHardDisk.createBaseStorage(anyLong(), anyLong())).andReturn(progress);
+
+      replay(manager, oldMachine, oldAttachment, oldMedium, detachSession, machine, vBox, medium, newHardDisk, progress);
+
+      IMedium newDisk = new CreateMediumIfNotAlreadyExists(Suppliers.ofInstance(manager), true).apply(hardDisk);
+
+      verify(machine, oldMachine, oldAttachment, detachSession, oldMedium, vBox, medium);
+      assertNotSame(newDisk, medium);
+   }
+
    @Test(expectedExceptions = IllegalStateException.class)
    public void testFailWhenMediumExistsAndNotUsingOverwrite() throws Exception {
       HardDisk hardDisk = createTestHardDisk();
@@ -130,7 +200,7 @@
 
       replay(manager, machine, vBox, medium, newHardDisk, progress);
 
-      new CreateMediumIfNotAlreadyExists(manager, false).apply(hardDisk);
+      new CreateMediumIfNotAlreadyExists(Suppliers.ofInstance(manager), false).apply(hardDisk);
    }
 
    @Test(expectedExceptions = VBoxException.class)
@@ -155,7 +225,7 @@
 
       replay(manager, machine, vBox, medium);
 
-      new CreateMediumIfNotAlreadyExists(manager, true).apply(hardDisk);
+      new CreateMediumIfNotAlreadyExists(Suppliers.ofInstance(manager), true).apply(hardDisk);
    }
 
    private HardDisk createTestHardDisk() {
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToHardwareTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToHardwareTest.java
index 6a60144..0b0989d 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToHardwareTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToHardwareTest.java
@@ -33,6 +33,8 @@
 import org.virtualbox_4_1.IVirtualBox;
 import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Suppliers;
+
 @Test(groups = "unit")
 public class IMachineToHardwareTest {
 
@@ -57,7 +59,7 @@
 
       replay(vbm, vBox, vm, guestOsType);
 
-      Hardware hardware = new IMachineToHardware(vbm).apply(vm);
+      Hardware hardware = new IMachineToHardware(Suppliers.ofInstance(vbm)).apply(vm);
 
       assertEquals(hardware.getId(), machineId);
       assertEquals(hardware.getProviderId(), machineId);
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToImageTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToImageTest.java
index 8ff77d2..af114a9 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToImageTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToImageTest.java
@@ -40,6 +40,7 @@
 import org.virtualbox_4_1.IVirtualBox;
 import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Suppliers;
 import com.google.inject.Guice;
 
 @Test(groups = "unit")
@@ -67,7 +68,7 @@
 
       replay(vbm, vBox, vm, guestOsType);
 
-      IMachineToImage fn = new IMachineToImage(vbm, map);
+      IMachineToImage fn = new IMachineToImage(Suppliers.ofInstance(vbm), map);
 
       Image image = fn.apply(vm);
 
@@ -98,7 +99,7 @@
 
       replay(vbm, vBox, vm, guestOsType);
 
-      IMachineToImage fn = new IMachineToImage(vbm, map);
+      IMachineToImage fn = new IMachineToImage(Suppliers.ofInstance(vbm), map);
 
       Image image = fn.apply(vm);
 
@@ -129,7 +130,9 @@
 
       replay(vbm, vBox, vm, guestOsType);
 
-      Image image = new IMachineToImage(vbm, map).apply(vm);
+      IMachineToImage fn = new IMachineToImage(Suppliers.ofInstance(vbm), map);
+
+      Image image = fn.apply(vm);
 
       assertEquals(image.getOperatingSystem().getDescription(), "SomeOtherOs 2.04");
       assertEquals(image.getOperatingSystem().getVersion(), "");
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadataTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadataTest.java
index 62c454f..ce590ca 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadataTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadataTest.java
@@ -31,6 +31,7 @@
 import org.virtualbox_4_1.MachineState;
 import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableSet;
 
 public class IMachineToNodeMetadataTest {
@@ -47,7 +48,7 @@
 
       VirtualBox virtualBox = new VirtualBox();
       IMachineToNodeMetadata parser = new IMachineToNodeMetadata();
-      IMachineToHardware hwParser = new IMachineToHardware(manager);
+      IMachineToHardware hwParser = new IMachineToHardware(Suppliers.ofInstance(manager));
 
       // hwParser.apply()
 
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToVmSpecTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToVmSpecTest.java
new file mode 100644
index 0000000..da62099
--- /dev/null
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToVmSpecTest.java
@@ -0,0 +1,102 @@
+/**
+ * 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.virtualbox.functions;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.classextension.EasyMock.createNiceMock;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.virtualbox.domain.HardDisk;
+import org.jclouds.virtualbox.domain.IsoImage;
+import org.jclouds.virtualbox.domain.StorageController;
+import org.jclouds.virtualbox.domain.VmSpec;
+import org.testng.annotations.Test;
+import org.virtualbox_4_1.DeviceType;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.IMedium;
+import org.virtualbox_4_1.IMediumAttachment;
+import org.virtualbox_4_1.IStorageController;
+import org.virtualbox_4_1.StorageBus;
+import org.virtualbox_4_1.VirtualBoxManager;
+
+import com.google.common.collect.Lists;
+
+@Test(groups = "unit")
+public class IMachineToVmSpecTest {
+
+   private static final String PATH_TO_DVD = "/path/to/dvd";
+   private static final String PATH_TO_HD = "/path/to/hd";
+   private static final StorageBus CONTROLLER_BUS = StorageBus.IDE;
+   private static final long MEMORY_SIZE = 512L;
+   private static final String VM_NAME = "test";
+   private static final String CONTROLLER_NAME = "IDE Controller";
+   private static final String VM_ID = "test";
+
+   @Test
+   public void testConvert() throws Exception {
+
+      VirtualBoxManager vbm = createNiceMock(VirtualBoxManager.class);
+      IStorageController iStorageController = createNiceMock(IStorageController.class); 
+      
+      IMediumAttachment iMediumAttachment = createNiceMock(IMediumAttachment.class); 
+      IMedium hd = createNiceMock(IMedium.class);
+      IMedium dvd = createNiceMock(IMedium.class);
+
+      IMachine vm = createNiceMock(IMachine.class);
+
+      expect(vm.getStorageControllers()).andReturn(Lists.newArrayList(iStorageController)).anyTimes();
+      expect(iStorageController.getName()).andReturn(CONTROLLER_NAME).anyTimes();
+      expect(iStorageController.getBus()).andReturn(CONTROLLER_BUS).anyTimes();
+      expect(vm.getMediumAttachmentsOfController(CONTROLLER_NAME)).andReturn(Lists.newArrayList(iMediumAttachment)).anyTimes();
+      expect(iMediumAttachment.getPort()).andReturn(0).once();
+      expect(iMediumAttachment.getDevice()).andReturn(0).once();
+      
+      expect(iMediumAttachment.getMedium()).andReturn(hd);
+      expect(hd.getDeviceType()).andReturn(DeviceType.HardDisk).once();
+      expect(hd.getLocation()).andReturn(PATH_TO_HD).once();
+      
+      expect(iMediumAttachment.getMedium()).andReturn(dvd);
+      expect(dvd.getDeviceType()).andReturn(DeviceType.DVD).once();
+      expect(dvd.getLocation()).andReturn(PATH_TO_DVD).once();
+
+      expect(vm.getName()).andReturn(VM_NAME).anyTimes();
+      expect(vm.getId()).andReturn(VM_ID).anyTimes();
+      expect(vm.getMemorySize()).andReturn(MEMORY_SIZE).anyTimes();
+      
+      replay(vbm, iStorageController, iMediumAttachment, hd, dvd, vm);
+
+      VmSpec vmSpec = new IMachineToVmSpec().apply(vm);
+      
+      assertEquals(vmSpec.getVmName(), VM_NAME);
+      assertEquals(vmSpec.getVmId(), VM_ID);
+      assertEquals(vmSpec.getMemory(), MEMORY_SIZE);
+      for(StorageController controller : vmSpec.getControllers()) {
+         assertEquals(controller.getName(), CONTROLLER_NAME);
+         assertEquals(controller.getBus(), CONTROLLER_BUS);
+         for (HardDisk hardDisk : controller.getHardDisks()) {
+            assertEquals(hardDisk.getDiskPath(), PATH_TO_HD);           
+         }
+         for (IsoImage iso : controller.getIsoImages()) {
+            assertEquals(iso.getSourcePath(), PATH_TO_DVD);           
+         }
+      }
+   }
+}
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunningTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunningTest.java
index fa1f0c9..a219759 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunningTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunningTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
 import static org.easymock.EasyMock.expect;
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfacesLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfacesLiveTest.java
index 30c9196..09388f1 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfacesLiveTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/RetrieveActiveBridgedInterfacesLiveTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,18 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions;
 
-import static org.jclouds.virtualbox.experiment.TestUtils.computeServiceForLocalhostAndGuest;
 import static org.jclouds.virtualbox.functions.RetrieveActiveBridgedInterfaces.retrieveBridgedInterfaceNames;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 
 import java.util.List;
 
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.domain.Credentials;
 import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
 import org.testng.annotations.Test;
 
@@ -38,48 +34,31 @@
  */
 @Test(groups = "live", singleThreaded = true, testName = "RetrieveActiveBridgedInterfacesLiveTest")
 public class RetrieveActiveBridgedInterfacesLiveTest extends BaseVirtualBoxClientLiveTest {
-   
-   public static final String TEST1 = "Name:            eth0\n"
-         + "GUID:            30687465-0000-4000-8000-00261834d0cb\n"
-         + "Dhcp:            Disabled\n" 
-         + "IPAddress:       209.x.x.x\n"
-         + "NetworkMask:     255.255.255.0\n"
-         + "IPV6Address:     fe80:0000:0000:0000:0226:18ff:fe34:d0cb\n"
-         + "IPV6NetworkMaskPrefixLength: 64\n"
-         + "HardwareAddress: 00:26:18:34:d0:cb\n"
-         + "MediumType:      Ethernet\n" 
-         + "Status:          Up\n"
-         + "VBoxNetworkName: HostInterfaceNetworking-eth0\n" 
-         + "\n"
-         + "Name:            vbox0\n"
-         + "GUID:            786f6276-0030-4000-8000-5a3ded993fed\n"
-         + "Dhcp:            Disabled\n" 
-         + "IPAddress:       192.168.56.1\n"
-         + "NetworkMask:     255.255.255.0\n"
-         + "IPV6Address: fe80:0000:0000:0000:0226:18ff:fe34:d0cb\n" 
-         + "IPV6NetworkMaskPrefixLength: 0\n"
-         + "HardwareAddress: 5a:3d:ed:99:3f:ed\n"
-         + "MediumType:      Ethernet\n" 
-         + "Status:          Down\n"
-         + "VBoxNetworkName: HostInterfaceNetworking-vbox0\n";
-   
-   public static final List<String> expectedBridgedInterfaces = ImmutableList.of("eth0", "vbox0");
 
-   private String guestId = "guest";
-   private String hostId = "host";
+   public static final String TEST1 = "Name:            eth0\n"
+            + "GUID:            30687465-0000-4000-8000-00261834d0cb\n" + "Dhcp:            Disabled\n"
+            + "IPAddress:       209.x.x.x\n" + "NetworkMask:     255.255.255.0\n"
+            + "IPV6Address:     fe80:0000:0000:0000:0226:18ff:fe34:d0cb\n" + "IPV6NetworkMaskPrefixLength: 64\n"
+            + "HardwareAddress: 00:26:18:34:d0:cb\n" + "MediumType:      Ethernet\n" + "Status:          Up\n"
+            + "VBoxNetworkName: HostInterfaceNetworking-eth0\n" + "\n" + "Name:            vbox0\n"
+            + "GUID:            786f6276-0030-4000-8000-5a3ded993fed\n" + "Dhcp:            Disabled\n"
+            + "IPAddress:       192.168.56.1\n" + "NetworkMask:     255.255.255.0\n"
+            + "IPV6Address: fe80:0000:0000:0000:0226:18ff:fe34:d0cb\n" + "IPV6NetworkMaskPrefixLength: 0\n"
+            + "HardwareAddress: 5a:3d:ed:99:3f:ed\n" + "MediumType:      Ethernet\n" + "Status:          Down\n"
+            + "VBoxNetworkName: HostInterfaceNetworking-vbox0\n";
+
+   public static final List<String> expectedBridgedInterfaces = ImmutableList.of("eth0", "vbox0");
 
    @Test
    public void retrieveBridgedInterfaceNamesTest() {
       List<String> activeBridgedInterfaceNames = retrieveBridgedInterfaceNames(TEST1);
       assertEquals(activeBridgedInterfaceNames, expectedBridgedInterfaces);
    }
-   
+
    @Test
    public void retrieveAvailableBridgedInterfaceInfoTest() {
-      ComputeServiceContext localHostContext = computeServiceForLocalhostAndGuest(
-            hostId, "localhost", guestId, "localhost", new Credentials("toor",
-                  "password"));
-      List<String> bridgedInterface = new RetrieveActiveBridgedInterfaces(localHostContext).apply(hostId);
+      List<String> bridgedInterface = context.utils().injector().getInstance(RetrieveActiveBridgedInterfaces.class)
+               .apply(host.get());
       assertFalse(bridgedInterface.isEmpty());
    }
 
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttachedTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttachedTest.java
index a03baa3..3be0490 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttachedTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttachedTest.java
@@ -33,6 +33,8 @@
 import org.virtualbox_4_1.IVirtualBox;
 import org.virtualbox_4_1.VirtualBoxManager;
 
+import com.google.common.base.Suppliers;
+
 /**
  * @author Andrea Turli
  */
@@ -64,7 +66,7 @@
       session.unlockMachine();
       replay(manager, machine, vBox, session, console, progress);
 
-      new TakeSnapshotIfNotAlreadyAttached(manager, snapshotName, snapshotDesc)
+      new TakeSnapshotIfNotAlreadyAttached(Suppliers.ofInstance(manager), snapshotName, snapshotDesc)
             .apply(machine);
 
       verify(machine);
@@ -94,7 +96,7 @@
       session.unlockMachine();
       replay(manager, machine, vBox, session, console, progress);
 
-      new TakeSnapshotIfNotAlreadyAttached(manager, snapshotName, snapshotDesc)
+      new TakeSnapshotIfNotAlreadyAttached(Suppliers.ofInstance(manager), snapshotName, snapshotDesc)
             .apply(machine);
 
       verify(machine);
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunningTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunningTest.java
index bcca15e..f89d4dc 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunningTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunningTest.java
@@ -25,29 +25,34 @@
 import static org.easymock.classextension.EasyMock.verify;
 import static org.testng.Assert.assertEquals;
 
+import java.net.URI;
+
 import org.eclipse.jetty.server.Server;
 import org.testng.annotations.Test;
 
 /**
- * @author Andrea Turli
+ * @author Andrea Turli, Adrian Cole
  */
 @Test(groups = "unit", singleThreaded = true, testName = "StartJettyIfNotAlreadyRunningTest")
 public class StartJettyIfNotAlreadyRunningTest {
-
-   private String basebaseResource = ".";
-   private int port = 8080;
-
    @Test
    public void testLaunchJettyServerWhenAlreadyRunningDoesntLaunchAgain() {
       Server jetty = createMock(Server.class);
+
+      String preconfigurationUrl = "http://foo:8080";
+      
       expect(jetty.getState()).andReturn(Server.STARTED);
       replay(jetty);
 
-      assertEquals(new StartJettyIfNotAlreadyRunning(jetty, port).apply(basebaseResource), jetty);
+      StartJettyIfNotAlreadyRunning starter = new StartJettyIfNotAlreadyRunning(jetty, preconfigurationUrl);
+      starter.start();
+
+      assertEquals(starter.get(), URI.create(preconfigurationUrl));
       verify(jetty);
 
    }
 
+
    @Test
    public void testLaunchJettyServerWhenNotRunningStartsJettyOnCorrectHostPortAndBasedir() {
       // TODO: all yours!
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningTest.java
index 6328cf1..c6645ce 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningTest.java
@@ -19,147 +19,92 @@
 
 package org.jclouds.virtualbox.functions.admin;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.ComputeServiceContextFactory;
-import org.jclouds.compute.domain.ExecResponse;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.domain.Credentials;
-import org.jclouds.domain.LoginCredentials;
-import org.jclouds.net.IPSocket;
-import org.jclouds.ssh.ConfiguresSshClient;
-import org.jclouds.ssh.SshClient;
-import org.testng.annotations.Test;
-import org.virtualbox_4_1.VirtualBoxManager;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.classextension.EasyMock.createMock;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.easymock.classextension.EasyMock.verify;
+import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot;
 
 import java.net.URI;
 
-import static org.easymock.EasyMock.expect;
-import static org.easymock.classextension.EasyMock.*;
-import static org.testng.Assert.assertEquals;
+import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.callables.RunScriptOnNode.Factory;
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.NodeState;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.predicates.RetryIfSocketNotYetOpen;
+import org.jclouds.net.IPSocket;
+import org.jclouds.scriptbuilder.domain.Statements;
+import org.testng.annotations.Test;
+import org.virtualbox_4_1.VirtualBoxManager;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Suppliers;
 
 @Test(groups = "unit", singleThreaded = true, testName = "StartVBoxIfNotAlreadyRunningTest")
 public class StartVBoxIfNotAlreadyRunningTest {
 
-   @Test(expectedExceptions = IllegalStateException.class)
-   public void testStartVboxThrowsIllegalStateExceptionIfTheNodeIdConfiguredIsntAround() throws Exception {
-
-      ComputeService compute = new ComputeServiceContextFactory().createContext("stub", "foo", "bar")
-            .getComputeService();
-
-      VirtualBoxManager manager = createMock(VirtualBoxManager.class);
-      @SuppressWarnings("unchecked")
-      Predicate<IPSocket> socketTester = createMock(Predicate.class);
-      String hostId = "hostId";
-      URI endpointUri = URI.create("http://localhost:18083/");
-      Credentials localhostCredentials = new Credentials("toor", "password");
-
-      manager.connect(endpointUri.toASCIIString(), localhostCredentials.identity, localhostCredentials.credential);
-
-      replay(socketTester);
-      replay(manager);
-
-      new StartVBoxIfNotAlreadyRunning(compute, manager, socketTester, hostId, localhostCredentials).apply(endpointUri);
-
-   }
-
+   @SuppressWarnings("unchecked")
    @Test
    public void testStartVboxConnectsToManagerWhenPortAlreadyListening() throws Exception {
-
-      ComputeService compute = new ComputeServiceContextFactory().createContext("stub", "foo", "bar")
-            .getComputeService();
-
-      // TODO: possibly better to use a defined name as opposed to an id, since
-      // most compute services the id is not predictable.
-      NodeMetadata node = Iterables.getOnlyElement(compute.createNodesInGroup("foo", 1));
-      String hostId = node.getId();
-
       VirtualBoxManager manager = createMock(VirtualBoxManager.class);
-      @SuppressWarnings("unchecked")
-      Predicate<IPSocket> socketTester = createMock(Predicate.class);
-      URI endpointUri = URI.create("http://localhost:18083/");
-      Credentials localhostCredentials = new Credentials("toor", "password");
+      Factory runScriptOnNodeFactory = createMock(Factory.class);
+      RetryIfSocketNotYetOpen client = createMock(RetryIfSocketNotYetOpen.class);
+      NodeMetadata host = new NodeMetadataBuilder().id("host").state(NodeState.RUNNING).build();
+      URI provider = URI.create("http://localhost:18083/");
+      String identity = "adminstrator";
+      String credential = "12345";
 
-      expect(socketTester.apply(new IPSocket(endpointUri.getHost(), endpointUri.getPort()))).andReturn(true);
-      manager.connect(endpointUri.toASCIIString(), localhostCredentials.identity, localhostCredentials.credential);
+      expect(client.apply(new IPSocket(provider.getHost(), provider.getPort()))).andReturn(true);
 
-      replay(socketTester);
-      replay(manager);
+      manager.connect(provider.toASCIIString(), identity, credential);
 
-      assertEquals(
-            new StartVBoxIfNotAlreadyRunning(compute, manager, socketTester, hostId, localhostCredentials)
-                  .apply(endpointUri),
-            manager);
+      replay(manager, runScriptOnNodeFactory, client);
 
-      verify(socketTester);
-      verify(manager);
-   }
+      new StartVBoxIfNotAlreadyRunning((Function) Functions.constant(manager), runScriptOnNodeFactory, client,
+               Suppliers.ofInstance(host), provider, identity, credential).start();
 
-   @ConfiguresSshClient
-   static class StartingVBoxWhenNotRunningModule extends AbstractModule {
-
-      @Override
-      protected void configure() {
-         SshClient.Factory factory = createMock(SshClient.Factory.class);
-         SshClient client = createMock(SshClient.class);
-         // NOTE we may want to switch to a node supplier so that we can predict
-         // these values. Right now, it is node 2 since the above test made node
-         // 1.
-         IPSocket expectedSshSockectFor2ndCreatedNode = new IPSocket("144.175.1.2", 22);
-         LoginCredentials loginCredentials = new LoginCredentials("root", "password2", null, false);
-         expect(factory.create(expectedSshSockectFor2ndCreatedNode, loginCredentials)).andReturn(
-               client).times(2);
-
-         expect(client.getUsername()).andReturn(loginCredentials.identity).times(2);
-         expect(client.getHostAddress()).andReturn(expectedSshSockectFor2ndCreatedNode.getAddress()).times(2);
-
-         client.disconnect();
-         client.connect();
-         expect(client.exec("VBoxManage setproperty websrvauthlibrary null\n")).andReturn(new ExecResponse("", "", 0));
-
-         client.disconnect();
-         client.connect();
-         expect(client.exec("vboxwebsrv -t 10000 -v -b\n")).andReturn(new ExecResponse("", "", 0));
-
-         replay(factory);
-         replay(client);
-         bind(SshClient.Factory.class).toInstance(factory);
-
-      }
+      verify(manager, runScriptOnNodeFactory, client);
 
    }
 
+   @SuppressWarnings("unchecked")
    @Test
    public void testStartVboxDisablesPasswordAccessOnWebsrvauthlibraryStartsVboxwebsrvInBackgroundAndConnectsManagerWhenPortIsNotListening()
-         throws Exception {
-      ComputeService compute = new ComputeServiceContextFactory().createContext("stub", "foo", "bar",
-            ImmutableSet.<Module> of(new StartingVBoxWhenNotRunningModule())).getComputeService();
-      NodeMetadata node = Iterables.getOnlyElement(compute.createNodesInGroup("foo", 1));
-      String hostId = node.getId();
-
+            throws Exception {
       VirtualBoxManager manager = createMock(VirtualBoxManager.class);
-      @SuppressWarnings("unchecked")
-      Predicate<IPSocket> socketTester = createMock(Predicate.class);
-      Credentials localhostCredentials = new Credentials("toor", "password");
-      URI endpointUri = URI.create("http://localhost:18083/");
+      Factory runScriptOnNodeFactory = createMock(Factory.class);
+      RetryIfSocketNotYetOpen client = createMock(RetryIfSocketNotYetOpen.class);
+      RunScriptOnNode runScriptOnNode = createMock(RunScriptOnNode.class);
+      NodeMetadata host = new NodeMetadataBuilder().id("host").state(NodeState.RUNNING).operatingSystem(
+               OperatingSystem.builder().description("unix").build()).build();
+      URI provider = URI.create("http://localhost:18083/");
+      String identity = "adminstrator";
+      String credential = "12345";
 
-      expect(socketTester.apply(new IPSocket(endpointUri.getHost(), endpointUri.getPort()))).andReturn(false);
+      expect(client.apply(new IPSocket(provider.getHost(), provider.getPort()))).andReturn(false);
+      expect(
+               runScriptOnNodeFactory.create(host, Statements.exec("VBoxManage setproperty websrvauthlibrary null"),
+                        runAsRoot(false).wrapInInitScript(false))).andReturn(runScriptOnNode);
+      expect(runScriptOnNode.init()).andReturn(runScriptOnNode);
+      expect(runScriptOnNode.call()).andReturn(new ExecResponse("", "", 0));
+      
+      expect(
+               runScriptOnNodeFactory.create(host, Statements.exec("vboxwebsrv -t 10000 -v -b"), runAsRoot(false)
+                        .wrapInInitScript(false).blockOnComplete(false).nameTask("vboxwebsrv"))).andReturn(
+               runScriptOnNode);
+      expect(runScriptOnNode.init()).andReturn(runScriptOnNode);
+      expect(runScriptOnNode.call()).andReturn(new ExecResponse("", "", 0));
+      
+      manager.connect(provider.toASCIIString(), identity, credential);
 
-      manager.connect(endpointUri.toASCIIString(), localhostCredentials.identity, localhostCredentials.credential);
+      replay(manager, runScriptOnNodeFactory, runScriptOnNode, client);
+      new StartVBoxIfNotAlreadyRunning((Function) Functions.constant(manager), runScriptOnNodeFactory, client,
+               Suppliers.ofInstance(host), provider, identity, credential).start();
+      verify(manager, runScriptOnNodeFactory, runScriptOnNode, client);
 
-      replay(socketTester);
-      replay(manager);
-
-      assertEquals(
-            new StartVBoxIfNotAlreadyRunning(compute, manager, socketTester, hostId, localhostCredentials)
-                  .apply(endpointUri),
-            manager);
-
-      verify(socketTester);
-      verify(manager);
    }
-}
+}
\ No newline at end of file
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndDeleteItsMediaTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndDeleteItsMediaTest.java
index fb38eab..3946717 100644
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndDeleteItsMediaTest.java
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndDeleteItsMediaTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.virtualbox.functions.admin;
 
 import static org.easymock.EasyMock.expect;
@@ -33,7 +32,6 @@
 import org.jclouds.virtualbox.domain.NatAdapter;
 import org.jclouds.virtualbox.domain.StorageController;
 import org.jclouds.virtualbox.domain.VmSpec;
-import org.jclouds.virtualbox.util.PropertyUtils;
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.CleanupMode;
 import org.virtualbox_4_1.IMachine;
@@ -61,11 +59,10 @@
       List<IMedium> media = new ArrayList<IMedium>();
       List<IMedium> mediums = Collections.unmodifiableList(media);
       
-      String workingDir = PropertyUtils.getWorkingDirFromProperty();
       StorageController ideController = StorageController.builder().name(ideControllerName).bus(StorageBus.IDE)
-              .attachISO(0, 0, workingDir + "/ubuntu-11.04-server-i386.iso")
-              .attachHardDisk(HardDisk.builder().diskpath(workingDir + "/testadmin.vdi").controllerPort(0).deviceSlot(1).build())
-              .attachISO(1, 1, workingDir + "/VBoxGuestAdditions_4.1.2.iso").build();
+              .attachISO(0, 0, "/tmp/ubuntu-11.04-server-i386.iso")
+              .attachHardDisk(HardDisk.builder().diskpath("/tmp/testadmin.vdi").controllerPort(0).deviceSlot(1).build())
+              .attachISO(1, 1, "/tmp/VBoxGuestAdditions_4.1.2.iso").build();
       VmSpec vmSpecification = VmSpec.builder().id(vmId).name(vmName).memoryMB(512).osTypeId(osTypeId)
               .controller(ideController)
               .forceOverwrite(true)
@@ -83,7 +80,7 @@
 
       replay(manager, vBox, registeredMachine, progress);
 
-      new UnregisterMachineIfExistsAndDeleteItsMedia(manager).apply(vmSpecification);
+      new UnregisterMachineIfExistsAndDeleteItsMedia(vmSpecification).apply(registeredMachine);
    }
 
 }
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/IsLinkedClonesLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/IsLinkedClonesLiveTest.java
new file mode 100644
index 0000000..b4a86d8
--- /dev/null
+++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/IsLinkedClonesLiveTest.java
@@ -0,0 +1,108 @@
+/**
+ * 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.virtualbox.predicates;
+
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
+import org.jclouds.virtualbox.domain.HardDisk;
+import org.jclouds.virtualbox.domain.StorageController;
+import org.jclouds.virtualbox.domain.VmSpec;
+import org.jclouds.virtualbox.functions.CloneAndRegisterMachineFromIMachineIfNotAlreadyExists;
+import org.jclouds.virtualbox.functions.CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.virtualbox_4_1.CleanupMode;
+import org.virtualbox_4_1.IMachine;
+import org.virtualbox_4_1.StorageBus;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @author Andrea Turli
+ */
+@Test(groups = "live", singleThreaded = true, testName = "IsLinkedClonesLiveTest")
+public class IsLinkedClonesLiveTest extends BaseVirtualBoxClientLiveTest {
+
+   private static final boolean IS_LINKED_CLONE = true;
+   private String vmId = "jclouds-image-iso-1";
+   private String osTypeId = "";
+   private String ideControllerName = "IDE Controller";
+   private String cloneName;
+   private String vmName;
+   private StorageController masterStorageController;
+   private VmSpec masterSpec;
+   private VmSpec cloneSpec;
+
+   @Override
+   @BeforeClass(groups = "live")
+   public void setupClient() {
+      super.setupClient();
+      vmName = VIRTUALBOX_IMAGE_PREFIX 
+            + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, getClass().getSimpleName());
+
+      cloneName = VIRTUALBOX_IMAGE_PREFIX 
+            + "Clone#" + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, getClass().getSimpleName());
+
+      HardDisk hardDisk = HardDisk.builder().diskpath(adminDisk).autoDelete(true).controllerPort(0).deviceSlot(1)
+               .build();
+      masterStorageController = StorageController.builder().name(ideControllerName).bus(StorageBus.IDE).attachISO(0, 0,
+               operatingSystemIso).attachHardDisk(hardDisk).attachISO(1, 1, guestAdditionsIso).build();
+      masterSpec = VmSpec.builder().id(vmId).name(vmName).memoryMB(512).osTypeId(osTypeId).controller(
+               masterStorageController).forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();
+      
+      cloneSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(512).cleanUpMode(CleanupMode.Full)
+            .forceOverwrite(true).build();
+   }
+
+   @Test
+   public void testLinkedClone() {
+
+      IMachine master = context.utils().injector().getInstance(CreateAndRegisterMachineFromIsoIfNotAlreadyExists.class)
+               .apply(masterSpec);
+      IMachine clone = new CloneAndRegisterMachineFromIMachineIfNotAlreadyExists(manager, workingDir, cloneSpec,
+               IS_LINKED_CLONE).apply(master);
+
+      assertTrue(new IsLinkedClone(manager).apply(clone));
+   }
+
+   /*
+   public void testFullClone() {
+      IMachine master = context.utils().injector().getInstance(CreateAndRegisterMachineFromIsoIfNotAlreadyExists.class)
+               .apply(masterSpec);
+      IMachine clone = new CloneAndRegisterMachineFromIMachineIfNotAlreadyExists(manager, workingDir, cloneSpec,
+               !IS_LINKED_CLONE).apply(master);
+
+      assertFalse(new IsLinkedClone(manager).apply(clone));
+   }
+   */
+
+   @BeforeMethod
+   @AfterMethod
+   void cleanUpVms() {
+      for (VmSpec spec : ImmutableSet.of(cloneSpec, masterSpec))
+         this.undoVm(spec);
+   }
+}
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/SshAvailableLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/SshAvailableLiveTest.java
deleted file mode 100644
index cb62918..0000000
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/SshAvailableLiveTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.jclouds.virtualbox.predicates;
-
-import static org.jclouds.virtualbox.domain.ExecutionType.HEADLESS;
-import static org.jclouds.virtualbox.experiment.TestUtils.computeServiceForLocalhostAndGuest;
-import static org.jclouds.virtualbox.util.MachineUtils.applyForMachine;
-import static org.jclouds.virtualbox.util.MachineUtils.lockSessionOnMachineAndApply;
-import static org.testng.Assert.assertTrue;
-import static org.virtualbox_4_1.LockType.Shared;
-
-import java.util.concurrent.TimeUnit;
-
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.domain.Credentials;
-import org.jclouds.net.IPSocket;
-import org.jclouds.predicates.InetSocketAddressConnect;
-import org.jclouds.predicates.RetryablePredicate;
-import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
-import org.jclouds.virtualbox.domain.ExecutionType;
-import org.jclouds.virtualbox.domain.HardDisk;
-import org.jclouds.virtualbox.domain.StorageController;
-import org.jclouds.virtualbox.domain.VmSpec;
-import org.jclouds.virtualbox.functions.CreateAndInstallVm;
-import org.jclouds.virtualbox.functions.LaunchMachineIfNotAlreadyRunning;
-import org.jclouds.virtualbox.util.PropertyUtils;
-import org.testng.annotations.Test;
-import org.virtualbox_4_1.IMachine;
-import org.virtualbox_4_1.IProgress;
-import org.virtualbox_4_1.ISession;
-import org.virtualbox_4_1.StorageBus;
-import org.virtualbox_4_1.VirtualBoxManager;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-
-@Test(groups = "live", singleThreaded = true, testName = "SshAvailableLiveTest")
-public class SshAvailableLiveTest extends BaseVirtualBoxClientLiveTest {
-
-   private String guestId = "guest";
-   private String hostId = "host";
-
-   private String vmName = "jclouds-image-virtualbox-iso-to-machine-sshtest";
-
-   @Test
-   public void testSshDaemonIsRunning() {
-      VirtualBoxManager manager = (VirtualBoxManager) context.getProviderSpecificContext().getApi();
-      ComputeServiceContext localHostContext = computeServiceForLocalhostAndGuest(
-              hostId, "localhost", guestId, "localhost", new Credentials("toor", "password"));
-
-      getNodeWithSshDaemonRunning(manager, localHostContext);
-      ensureMachineIsLaunched(vmName);
-      RetryablePredicate<String> predicate = new RetryablePredicate<String>(
-              new SshAvailable(localHostContext), 5, 1,
-              TimeUnit.SECONDS);
-      assertTrue(predicate.apply(guestId));
-
-      lockSessionOnMachineAndApply(manager, Shared, vmName, new Function<ISession, Void>() {
-
-         @Override
-         public Void apply(ISession session) {
-            IProgress powerDownProgress = session.getConsole().powerDown();
-            powerDownProgress.waitForCompletion(-1);
-            return null;
-         }
-
-      });
-   }
-
-   private IMachine getNodeWithSshDaemonRunning(VirtualBoxManager manager, ComputeServiceContext localHostContext) {
-      try {
-         Predicate<IPSocket> socketTester = new RetryablePredicate<IPSocket>(
-                 new InetSocketAddressConnect(), 10, 1, TimeUnit.SECONDS);
-         String vmId = "jclouds-image-iso-2";
-
-         String workingDir = PropertyUtils.getWorkingDirFromProperty();
-         StorageController ideController = StorageController.builder().name("IDE Controller").bus(StorageBus.IDE)
-                 .attachISO(0, 0, workingDir + "/ubuntu-11.04-server-i386.iso")
-                 .attachHardDisk(HardDisk.builder().diskpath(workingDir + "/testadmin.vdi").controllerPort(0).deviceSlot(1).build()).build();
-         VmSpec vmSpecification = VmSpec.builder().id(vmId).name(vmName).osTypeId("")
-         		  .memoryMB(512)
-                 .controller(ideController)
-                 .forceOverwrite(true).build();
-
-         return new CreateAndInstallVm(manager, guestId, localHostContext,
-                 hostId, socketTester, "127.0.0.1", 8080, HEADLESS).apply(vmSpecification);
-      } catch (IllegalStateException e) {
-         // already created
-         return manager.getVBox().findMachine(vmName);
-      }
-   }
-
-   private void ensureMachineIsLaunched(String vmName) {
-      applyForMachine(manager, vmName, new LaunchMachineIfNotAlreadyRunning(
-              manager, ExecutionType.GUI, ""));
-   }
-
-}
\ No newline at end of file
diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/util/PropertyUtils.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/util/PropertyUtils.java
deleted file mode 100644
index fe34719..0000000
--- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/util/PropertyUtils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.virtualbox.util;
-
-import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR;
-
-public class PropertyUtils {
-
-   public static String getWorkingDirFromProperty() {
-      final String defaultWorkingDir = System.getProperty("user.home") + "/jclouds-virtualbox-test";
-      return System.getProperty(VIRTUALBOX_WORKINGDIR, defaultWorkingDir);
-   }
-
-}
diff --git a/sandbox-apis/virtualbox/src/test/resources/logback.xml b/sandbox-apis/virtualbox/src/test/resources/logback.xml
index d68d005..c1bb1b7 100644
--- a/sandbox-apis/virtualbox/src/test/resources/logback.xml
+++ b/sandbox-apis/virtualbox/src/test/resources/logback.xml
@@ -1,15 +1,64 @@
 <?xml version="1.0"?>

 <configuration scan="false">

-  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

-    <encoder>

-      <pattern>%d{HH:mm:ss.SSS} - %msg%n</pattern>

-    </encoder>

-  </appender>

-  <!-- turn OFF all logging (children can override) -->

-  <root level="OFF">

-    <appender-ref ref="STDOUT"/>

-  </root>

-  <logger name="jclouds.compute" level="debug"/>

-  <logger name="jclouds.headers" level="debug"/>

-  <logger name="jclouds.ssh" level="debug"/>

+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">

+        <file>target/test-data/jclouds.log</file>

+

+        <layout class="ch.qos.logback.classic.PatternLayout">

+            <Pattern>%d %-5p [%c] (%t) %m%n</Pattern>

+        </layout>

+    </appender>

+

+    <appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">

+        <file>target/test-data/jclouds-wire.log</file>

+

+        <layout class="ch.qos.logback.classic.PatternLayout">

+            <Pattern>%d %-5p [%c] (%t) %m%n</Pattern>

+        </layout>

+    </appender>

+

+    <appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">

+        <file>target/test-data/jclouds-compute.log</file>

+

+        <layout class="ch.qos.logback.classic.PatternLayout">

+            <Pattern>%d %-5p [%c] (%t) %m%n</Pattern>

+        </layout>

+    </appender>

+    

+    <appender name="SSHFILE" class="ch.qos.logback.core.FileAppender">

+        <file>target/test-data/jclouds-ssh.log</file>

+

+        <layout class="ch.qos.logback.classic.PatternLayout">

+            <Pattern>%d %-5p [%c] (%t) %m%n</Pattern>

+        </layout>

+    </appender>

+

+    <root>

+        <level value="warn" />

+    </root>

+

+    <logger name="org.jclouds">

+        <level value="DEBUG" />

+        <appender-ref ref="FILE" />

+    </logger>

+

+    <logger name="jclouds.wire">

+        <level value="DEBUG" />

+        <appender-ref ref="WIREFILE" />

+    </logger>

+

+    <logger name="jclouds.headers">

+        <level value="DEBUG" />

+        <appender-ref ref="WIREFILE" />

+    </logger>

+

+    <logger name="jclouds.compute">

+        <level value="DEBUG" />

+        <appender-ref ref="COMPUTEFILE" />

+    </logger>

+    

+    <logger name="jclouds.ssh">

+        <level value="DEBUG" />

+        <appender-ref ref="SSHFILE" />

+    </logger>

+

 </configuration>

diff --git a/sandbox-providers/aws-elb/pom.xml b/sandbox-providers/aws-elb/pom.xml
index 8e9f11f..673d042 100644
--- a/sandbox-providers/aws-elb/pom.xml
+++ b/sandbox-providers/aws-elb/pom.xml
@@ -35,12 +35,14 @@
 
     <properties>
         <test.aws-elb.endpoint>https://elasticloadbalancing.us-east-1.amazonaws.com</test.aws-elb.endpoint>
-        <test.aws-elb.apiversion>2010-07-01</test.aws-elb.apiversion>
+        <test.aws-elb.api-version>2010-07-01</test.aws-elb.api-version>
+        <test.aws-elb.build-version></test.aws-elb.build-version>
         <test.aws-elb.identity>${test.aws.identity}</test.aws-elb.identity>
         <test.aws-elb.credential>${test.aws.credential}</test.aws-elb.credential>
         <test.aws-elb.compute.provider>aws-ec2</test.aws-elb.compute.provider>
         <test.aws-elb.compute.endpoint>https://ec2.us-east-1.amazonaws.com</test.aws-elb.compute.endpoint>
-        <test.aws-elb.compute.apiversion>2010-06-15</test.aws-elb.compute.apiversion>
+        <test.aws-elb.compute.api-version>2010-06-15</test.aws-elb.compute.api-version>
+        <test.aws-elb.compute.build-version></test.aws-elb.compute.build-version>
         <test.aws-elb.compute.identity>${test.aws.identity}</test.aws-elb.compute.identity>
         <test.aws-elb.compute.credential>${test.aws.credential}</test.aws-elb.compute.credential>
         <test.aws-elb.compute.image-id></test.aws-elb.compute.image-id>
@@ -119,12 +121,14 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.aws-elb.endpoint>${test.aws-elb.endpoint}</test.aws-elb.endpoint>
-                                        <test.aws-elb.apiversion>${test.aws-elb.apiversion}</test.aws-elb.apiversion>
+                                        <test.aws-elb.api-version>${test.aws-elb.api-version}</test.aws-elb.api-version>
+                                        <test.aws-elb.build-version>${test.aws-elb.build-version}</test.aws-elb.build-version>
                                         <test.aws-elb.identity>${test.aws-elb.identity}</test.aws-elb.identity>
                                         <test.aws-elb.credential>${test.aws-elb.credential}</test.aws-elb.credential>
                                         <test.aws-elb.compute.provider>${test.aws-elb.compute.provider}</test.aws-elb.compute.provider>
                                         <test.aws-elb.compute.endpoint>${test.aws-elb.compute.endpoint}</test.aws-elb.compute.endpoint>
-                                        <test.aws-elb.compute.apiversion>${test.aws-elb.compute.apiversion}</test.aws-elb.compute.apiversion>
+                                        <test.aws-elb.compute.build-version>${test.aws-elb.compute.build-version}</test.aws-elb.compute.build-version>
+                                        <test.aws-elb.compute.api-version>${test.aws-elb.compute.api-version}</test.aws-elb.compute.api-version>
                                         <test.aws-elb.compute.identity>${test.aws-elb.compute.identity}</test.aws-elb.compute.identity>
                                         <test.aws-elb.compute.credential>${test.aws-elb.compute.credential}</test.aws-elb.compute.credential>
                                         <test.aws-elb.compute.image-id>${test.aws-elb.compute.image-id}</test.aws-elb.compute.image-id>
diff --git a/sandbox-providers/aws-simpledb/pom.xml b/sandbox-providers/aws-simpledb/pom.xml
index 002e722..8060295 100644
--- a/sandbox-providers/aws-simpledb/pom.xml
+++ b/sandbox-providers/aws-simpledb/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.aws-simpledb.endpoint>https://sdb.amazonaws.com</test.aws-simpledb.endpoint>
-        <test.aws-simpledb.apiversion>2009-04-15</test.aws-simpledb.apiversion>
+        <test.aws-simpledb.api-version>2009-04-15</test.aws-simpledb.api-version>
+        <test.aws-simpledb.build-version></test.aws-simpledb.build-version>
         <test.aws-simpledb.identity>${test.aws.identity}</test.aws-simpledb.identity>
         <test.aws-simpledb.credential>${test.aws.credential}</test.aws-simpledb.credential>
     </properties>
@@ -93,7 +94,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.aws-simpledb.endpoint>${test.aws-simpledb.endpoint}</test.aws-simpledb.endpoint>
-                                        <test.aws-simpledb.apiversion>${test.aws-simpledb.apiversion}</test.aws-simpledb.apiversion>
+                                        <test.aws-simpledb.api-version>${test.aws-simpledb.api-version}</test.aws-simpledb.api-version>
+                                        <test.aws-simpledb.build-version>${test.aws-simpledb.build-version}</test.aws-simpledb.build-version>
                                         <test.aws-simpledb.identity>${test.aws-simpledb.identity}</test.aws-simpledb.identity>
                                         <test.aws-simpledb.credential>${test.aws-simpledb.credential}</test.aws-simpledb.credential>
                                     </systemPropertyVariables>
diff --git a/sandbox-providers/azurequeue/pom.xml b/sandbox-providers/azurequeue/pom.xml
index 48c289e..195da18 100644
--- a/sandbox-providers/azurequeue/pom.xml
+++ b/sandbox-providers/azurequeue/pom.xml
@@ -35,7 +35,8 @@
 
     <properties>
         <test.azurequeue.endpoint>https://{identity}.queue.core.windows.net</test.azurequeue.endpoint>
-        <test.azurequeue.apiversion>2009-09-19</test.azurequeue.apiversion>
+        <test.azurequeue.api-version>2009-09-19</test.azurequeue.api-version>
+        <test.azurequeue.build-version></test.azurequeue.build-version>
         <test.azurequeue.identity>${test.azure.identity}</test.azurequeue.identity>
         <test.azurequeue.credential>${test.azure.credential}</test.azurequeue.credential>
     </properties>
@@ -86,7 +87,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.azurequeue.endpoint>${test.azurequeue.endpoint}</test.azurequeue.endpoint>
-                                        <test.azurequeue.apiversion>${test.azurequeue.apiversion}</test.azurequeue.apiversion>
+                                        <test.azurequeue.api-version>${test.azurequeue.api-version}</test.azurequeue.api-version>
+                                        <test.azurequeue.build-version>${test.azurequeue.build-version}</test.azurequeue.build-version>
                                         <test.azurequeue.identity>${test.azurequeue.identity}</test.azurequeue.identity>
                                         <test.azurequeue.credential>${test.azurequeue.credential}</test.azurequeue.credential>
                                     </systemPropertyVariables>
diff --git a/sandbox-providers/azurequeue/src/test/java/org/jclouds/azurequeue/AzureQueueClientLiveTest.java b/sandbox-providers/azurequeue/src/test/java/org/jclouds/azurequeue/AzureQueueClientLiveTest.java
index 49b0e94..054dc02 100644
--- a/sandbox-providers/azurequeue/src/test/java/org/jclouds/azurequeue/AzureQueueClientLiveTest.java
+++ b/sandbox-providers/azurequeue/src/test/java/org/jclouds/azurequeue/AzureQueueClientLiveTest.java
@@ -60,15 +60,15 @@
    protected String identity;
    protected String credential;
    protected String endpoint;
-   protected String apiversion;
+   protected String apiVersion;
 
    protected void setupCredentials() {
       identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
       credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
                + ".credential");
       endpoint = checkNotNull(System.getProperty("test." + provider + ".endpoint"), "test." + provider + ".endpoint");
-      apiversion = checkNotNull(System.getProperty("test." + provider + ".apiversion"), "test." + provider
-               + ".apiversion");
+     apiVersion = checkNotNull(System.getProperty("test." + provider + ".api-version"), "test." + provider
+               + ".api-version");
    }
 
    protected Properties setupProperties() {
@@ -78,7 +78,7 @@
       overrides.setProperty(provider + ".identity", identity);
       overrides.setProperty(provider + ".credential", credential);
       overrides.setProperty(provider + ".endpoint", endpoint);
-      overrides.setProperty(provider + ".apiversion", apiversion);
+      overrides.setProperty(provider + ".api-version", apiVersion);
       return overrides;
    }
 
diff --git a/sandbox-providers/boxdotnet/pom.xml b/sandbox-providers/boxdotnet/pom.xml
index 8e87e04..e36166e 100644
--- a/sandbox-providers/boxdotnet/pom.xml
+++ b/sandbox-providers/boxdotnet/pom.xml
@@ -49,7 +49,8 @@
 
     <properties>
         <test.boxdotnet.endpoint>https://www.box.net/api/1.0/rest</test.boxdotnet.endpoint>
-        <test.boxdotnet.apiversion>1.0</test.boxdotnet.apiversion>
+        <test.boxdotnet.api-version>1.0</test.boxdotnet.api-version>
+        <test.boxdotnet.build-version></test.boxdotnet.build-version>
         <test.boxdotnet.identity>FIXME</test.boxdotnet.identity>
         <test.boxdotnet.credential>FIXME</test.boxdotnet.credential>
     </properties>
@@ -97,7 +98,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.boxdotnet.endpoint>${test.boxdotnet.endpoint}</test.boxdotnet.endpoint>
-                                        <test.boxdotnet.apiversion>${test.boxdotnet.apiversion}</test.boxdotnet.apiversion>
+                                        <test.boxdotnet.api-version>${test.boxdotnet.api-version}</test.boxdotnet.api-version>
+                                        <test.boxdotnet.build-version>${test.boxdotnet.build-version}</test.boxdotnet.build-version>
                                         <test.boxdotnet.identity>${test.boxdotnet.identity}</test.boxdotnet.identity>
                                         <test.boxdotnet.credential>${test.boxdotnet.credential}</test.boxdotnet.credential>
                                     </systemPropertyVariables>
diff --git a/sandbox-providers/dunkel-vcd/pom.xml b/sandbox-providers/dunkel-vcd/pom.xml
index 7c25bc9..71d887b 100644
--- a/sandbox-providers/dunkel-vcd/pom.xml
+++ b/sandbox-providers/dunkel-vcd/pom.xml
@@ -34,7 +34,8 @@
 
     <properties>
         <test.dunkel-vcd.endpoint>https://vcd.dunkel.de/api</test.dunkel-vcd.endpoint>
-        <test.dunkel-vcd.apiversion>1.0</test.dunkel-vcd.apiversion>
+        <test.dunkel-vcd.api-version>1.0</test.dunkel-vcd.api-version>
+        <test.dunkel-vcd.build-version></test.dunkel-vcd.build-version>
         <test.dunkel-vcd.identity>FIXME_IDENTITY</test.dunkel-vcd.identity>
         <test.dunkel-vcd.credential>FIXME_CREDENTIAL</test.dunkel-vcd.credential>
         <test.dunkel-vcd.image-id></test.dunkel-vcd.image-id>
@@ -99,7 +100,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.dunkel-vcd.endpoint>${test.dunkel-vcd.endpoint}</test.dunkel-vcd.endpoint>
-                                        <test.dunkel-vcd.apiversion>${test.dunkel-vcd.apiversion}</test.dunkel-vcd.apiversion>
+                                        <test.dunkel-vcd.api-version>${test.dunkel-vcd.api-version}</test.dunkel-vcd.api-version>
+                                        <test.dunkel-vcd.build-version>${test.dunkel-vcd.build-version}</test.dunkel-vcd.build-version>
                                         <test.dunkel-vcd.identity>${test.dunkel-vcd.identity}</test.dunkel-vcd.identity>
                                         <test.dunkel-vcd.credential>${test.dunkel-vcd.credential}</test.dunkel-vcd.credential>
                                         <test.dunkel-vcd.image-id>${test.dunkel-vcd.image-id}</test.dunkel-vcd.image-id>
diff --git a/sandbox-providers/glesys/pom.xml b/sandbox-providers/glesys/pom.xml
index 76873f5..3a03d0d 100644
--- a/sandbox-providers/glesys/pom.xml
+++ b/sandbox-providers/glesys/pom.xml
@@ -49,7 +49,8 @@
 
   <properties>
     <test.glesys.endpoint>https://api.glesys.com</test.glesys.endpoint>
-    <test.glesys.apiversion>1</test.glesys.apiversion>
+    <test.glesys.api-version>1</test.glesys.api-version>
+    <test.glesys.build-version></test.glesys.build-version>
     <test.glesys.identity>FIXME</test.glesys.identity>
     <test.glesys.credential>FIXME</test.glesys.credential>
     <test.glesys.image-id></test.glesys.image-id>
@@ -107,7 +108,8 @@
                 <configuration>
                   <systemPropertyVariables>
                     <test.glesys.endpoint>${test.glesys.endpoint}</test.glesys.endpoint>
-                    <test.glesys.apiversion>${test.glesys.apiversion}</test.glesys.apiversion>
+                    <test.glesys.api-version>${test.glesys.api-version}</test.glesys.api-version>
+                    <test.glesys.build-version>${test.glesys.build-version}</test.glesys.build-version>
                     <test.glesys.identity>${test.glesys.identity}</test.glesys.identity>
                     <test.glesys.credential>${test.glesys.credential}</test.glesys.credential>
                     <test.glesys.image-id>${test.glesys.image-id}</test.glesys.image-id>
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/GleSYSAsyncClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/GleSYSAsyncClient.java
index 913b9fa..2249c4d 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/GleSYSAsyncClient.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/GleSYSAsyncClient.java
@@ -18,10 +18,7 @@
  */
 package org.jclouds.glesys;
 
-import org.jclouds.glesys.features.ArchiveAsyncClient;
-import org.jclouds.glesys.features.DomainAsyncClient;
-import org.jclouds.glesys.features.IpAsyncClient;
-import org.jclouds.glesys.features.ServerAsyncClient;
+import org.jclouds.glesys.features.*;
 import org.jclouds.rest.annotations.Delegate;
 
 /**
@@ -58,4 +55,10 @@
    @Delegate
    DomainAsyncClient getDomainClient();
 
+   /**
+    * Provides asynchronous access to E-Mail features.
+    */
+   @Delegate
+   EmailAsyncClient getEmailClient();
+   
 }
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/GleSYSClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/GleSYSClient.java
index 91b9a6d..65e0d61 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/GleSYSClient.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/GleSYSClient.java
@@ -19,10 +19,7 @@
 package org.jclouds.glesys;
 
 import org.jclouds.concurrent.Timeout;
-import org.jclouds.glesys.features.ArchiveClient;
-import org.jclouds.glesys.features.DomainClient;
-import org.jclouds.glesys.features.IpClient;
-import org.jclouds.glesys.features.ServerClient;
+import org.jclouds.glesys.features.*;
 import org.jclouds.rest.annotations.Delegate;
 
 import java.util.concurrent.TimeUnit;
@@ -60,5 +57,12 @@
     * Provides synchronous access to DNS features.
     */
    @Delegate
-   DomainClient getDomainClient();   
+   DomainClient getDomainClient();
+
+   /**
+    * Provides synchronous access to E-Mail features.
+    */
+   @Delegate
+   EmailClient getEmailClient();
+
 }
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/config/GleSYSParserModule.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/config/GleSYSParserModule.java
index a979df0..1818569 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/config/GleSYSParserModule.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/config/GleSYSParserModule.java
@@ -18,19 +18,20 @@
  */
 package org.jclouds.glesys.config;
 
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import javax.inject.Singleton;
+
+import org.jclouds.glesys.domain.ServerState;
+import org.jclouds.glesys.domain.ServerUptime;
+import org.jclouds.glesys.functions.internal.GleSYSTypeAdapters;
+import org.jclouds.glesys.functions.internal.GlesysDateAdapter;
+import org.jclouds.json.config.GsonModule.DateAdapter;
+
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
-import org.jclouds.glesys.domain.ServerState;
-import org.jclouds.glesys.domain.ServerUptime;
-import org.jclouds.glesys.functions.internal.CustomDeserializers;
-import org.jclouds.glesys.functions.internal.GlesysDateAdapter;
-import org.jclouds.json.config.GsonModule;
-import org.jclouds.json.config.GsonModule.DateAdapter;
-
-import javax.inject.Singleton;
-import java.lang.reflect.Type;
-import java.util.Map;
 
 /**
  * @author Adrian Cole
@@ -40,10 +41,8 @@
    @Provides
    @Singleton
    public Map<Type, Object> provideCustomAdapterBindings() {
-      return ImmutableMap.<Type, Object>of(
-            ServerState.class, new CustomDeserializers.ServerStateAdapter(),
-            ServerUptime.class, new CustomDeserializers.ServerUptimeAdaptor()
-      );
+      return ImmutableMap.<Type, Object> of(ServerState.class, new GleSYSTypeAdapters.ServerStateAdapter(),
+               ServerUptime.class, new GleSYSTypeAdapters.ServerUptimeAdapter());
    }
 
    @Override
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/config/GleSYSRestClientModule.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/config/GleSYSRestClientModule.java
index e9a5879..e9d9236 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/config/GleSYSRestClientModule.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/config/GleSYSRestClientModule.java
@@ -50,6 +50,7 @@
          .put(IpClient.class, IpAsyncClient.class)//
          .put(ArchiveClient.class, ArchiveAsyncClient.class)//
          .put(DomainClient.class, DomainAsyncClient.class)//
+         .put(EmailClient.class, EmailAsyncClient.class)//
          .build();
 
    public GleSYSRestClientModule() {
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Archive.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Archive.java
index 4f974b2..1d87820 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Archive.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Archive.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.domain;
 
 import com.google.common.base.Objects;
@@ -7,7 +25,7 @@
  * Information about an archive
  *
  * @author Adam Lowe
- * @see <a href= "https://customer.glesys.com/api.php?a=doc#archive_details" />
+ * @see <a href= "https://customer.glesys.com/api.php?a=doc#archive_list" />
  */
 public class Archive implements Comparable<Archive> {
    public static Builder builder() {
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveAllowedArguments.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveAllowedArguments.java
index e01940c..2def35f 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveAllowedArguments.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveAllowedArguments.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.domain;
 
 import com.google.common.base.Joiner;
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveDetails.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveDetails.java
index 30b1503..c3530a7 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveDetails.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveDetails.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.domain;
 
 /**
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Domain.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Domain.java
index 1b77fee..bdee0df 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Domain.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Domain.java
@@ -27,6 +27,7 @@
  * Domain data for a Glesys account.
  *
  * @author Adam Lowe
+ * @see <a href= "https://customer.glesys.com/api.php?a=doc#domain_list" />
  */
 public class Domain implements Comparable<Domain> {
    public static Builder builder() {
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/DomainRecord.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/DomainRecord.java
index 4ee0422..86b4fe6 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/DomainRecord.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/DomainRecord.java
@@ -24,6 +24,7 @@
  * DNS record data.
  *
  * @author Adam Lowe
+ * @see <a href= "https://customer.glesys.com/api.php?a=doc#domain_list_records" />
  */
 public class DomainRecord implements Comparable<DomainRecord> {
    public static Builder builder() {
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Email.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Email.java
new file mode 100644
index 0000000..3846c4e
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/Email.java
@@ -0,0 +1,213 @@
+/**
+ * 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.glesys.domain;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.SerializedName;
+
+import java.util.Date;
+
+/**
+ * Detailed information on an Email Account
+ * 
+ * @author Adam Lowe
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#email_list" />
+ */
+public class Email implements Comparable<Email> {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String account;
+      private String quota;
+      private String usedQuota;
+      private int antispamLevel;
+      private boolean antiVirus;
+      private boolean autoRespond;
+      private String autoRespondMessage;
+      private boolean autoRespondSaveEmail;
+      private Date created;
+      private Date modified;
+
+      public Builder account(String account) {
+         this.account = account;
+         return this;
+      }
+      
+      public Builder quota(String quota) {
+         this.quota = quota;
+         return this;
+      }
+
+      public Builder usedQuota(String usedQuota) {
+         this.usedQuota = usedQuota;
+         return this;
+      }
+      
+      public Builder antispamLevel(int antispamLevel) {
+         this.antispamLevel = antispamLevel;
+         return this;
+      }
+
+      public Builder antiVirus(boolean antiVirus) {
+         this.antiVirus = antiVirus;
+         return this;
+      }
+      
+      public Builder autoRespond(boolean autoRespond) {
+         this.autoRespond = autoRespond;
+         return this;
+      }
+      
+      public Builder autoRespondMessage(String autoRespondMessage) {
+         this.autoRespondMessage = autoRespondMessage;
+         return this;
+      }
+
+      public Builder autoRespondSaveEmail(boolean autoRespondSaveEmail) {
+         this.autoRespondSaveEmail = autoRespondSaveEmail;
+         return this;
+      }
+
+      public Builder created(Date created) {
+         this.created = created;
+         return this;
+      }
+
+      public Builder modified(Date modified) {
+         this.modified = modified;
+         return this;
+      }
+
+      public Email build() {
+         return new Email(account, quota, usedQuota, antispamLevel, antiVirus, autoRespond, autoRespondMessage,
+               autoRespondSaveEmail, created, modified);
+      }
+
+      public Builder fromEmail(Email in) {
+         return account(in.getAccount()).quota(in.getQuota()).usedQuota(in.getUsedQuota()).antispamLevel(in.getAntispamLevel()).
+               antiVirus(in.getAntiVirus()).autoRespond(in.getAutoRespond()).autoRespondMessage(in.getAutoRespondMessage()).
+               autoRespondSaveEmail(in.getAutoRespondSaveEmail()).created(in.getCreated()).modified(in.getModified());
+      }
+   }
+
+   @SerializedName("emailaccount")
+   private final String account;
+   private final String quota;
+   @SerializedName("usedquota")
+   private final String usedQuota;
+   @SerializedName("antispamlevel")
+   private final int antispamLevel;
+   @SerializedName("antivirus")
+   private final boolean antiVirus;
+   @SerializedName("autorespond")
+   private final boolean autoRespond;
+   @SerializedName("autorespondmessage")
+   private final String autoRespondMessage;
+   @SerializedName("autorespondsaveemail")
+   private final boolean autoRespondSaveEmail;
+   private final Date created;
+   private final Date modified;
+
+   public Email(String account, String quota, String usedQuota, int antispamLevel, boolean antiVirus, boolean autoRespond, String autoRespondMessage, boolean autoRespondSaveEmail, Date created, Date modified) {
+      this.account = account;
+      this.quota = quota;
+      this.usedQuota = usedQuota;
+      this.antispamLevel = antispamLevel;
+      this.antiVirus = antiVirus;
+      this.autoRespond = autoRespond;
+      this.autoRespondMessage = autoRespondMessage;
+      this.autoRespondSaveEmail = autoRespondSaveEmail;
+      this.created = created;
+      this.modified = modified;
+   }
+
+   public String getAccount() {
+      return account;
+   }
+
+   public String getQuota() {
+      return quota;
+   }
+
+   public String getUsedQuota() {
+      return usedQuota;
+   }
+
+   public int getAntispamLevel() {
+      return antispamLevel;
+   }
+
+   public boolean getAntiVirus() {
+      return antiVirus;
+   }
+
+   public boolean getAutoRespond() {
+      return autoRespond;
+   }
+
+   public String getAutoRespondMessage() {
+      return autoRespondMessage;
+   }
+
+   public boolean getAutoRespondSaveEmail() {
+      return autoRespondSaveEmail;
+   }
+
+   public Date getCreated() {
+      return created;
+   }
+
+   public Date getModified() {
+      return modified;
+   }
+   
+   @Override
+   public int compareTo(Email other) {
+      return account.compareTo(other.getAccount());
+   }
+   
+   @Override
+   public boolean equals(Object object) {
+      if (this == object) {
+         return true;
+      }
+      if (object instanceof Email) {
+         Email other = (Email) object;
+         return Objects.equal(account, other.account);
+      } else {
+         return false;
+      }
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(account);
+   }
+   
+   @Override
+   public String toString() {
+      return String.format("account=%s, quota=%s, usedquota=%s, antispamLevel=%d, " +
+            "antiVirus=%b, autoRespond=%b, autoRespondMessage=%s, autoRespondSaveEmail=%b, " +
+            "created=%s, modified=%s", account, quota, usedQuota, antispamLevel, antiVirus, autoRespond, autoRespondMessage,
+            autoRespondSaveEmail, created.toString(), modified.toString());
+   }
+
+}
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverview.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverview.java
new file mode 100644
index 0000000..708dcc1
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverview.java
@@ -0,0 +1,104 @@
+/**
+ * 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.glesys.domain;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Set;
+
+/**
+ * @author Adam Lowe
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#email_overview" />
+ */
+public class EmailOverview {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private EmailOverviewSummary summary;
+      private Set<EmailOverviewDomain> domains;
+
+      public Builder summary(EmailOverviewSummary summary) {
+         this.summary = summary;
+         return this;
+      }
+
+      public Builder domains(Set<EmailOverviewDomain> domains) {
+         this.domains = domains;
+         return this;
+      }
+
+      public Builder domains(EmailOverviewDomain... domains) {
+         return domains(ImmutableSet.copyOf(domains));
+      }
+
+      public EmailOverview build() {
+         return new EmailOverview(summary, domains);
+      }
+      
+      public Builder fromEmailOverview(EmailOverview in) {
+         return summary(in.getSummary()).domains(in.getDomains());
+      }
+   }
+
+   private EmailOverviewSummary summary;
+   private Set<EmailOverviewDomain> domains;
+
+   public EmailOverview(EmailOverviewSummary summary,  Set<EmailOverviewDomain> domains) {
+      this.summary = summary; 
+      this.domains = domains;
+   }
+
+   public EmailOverviewSummary getSummary() {
+      return summary;
+   }
+
+   public Set<EmailOverviewDomain> getDomains() {
+      return domains == null ? ImmutableSet.<EmailOverviewDomain>of() : domains;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(summary, domains);
+   }
+
+   @Override
+   public boolean equals(Object object) {
+      if (object == this) {
+         return true;
+      }
+      if (object instanceof EmailOverview) {
+         EmailOverview other = (EmailOverview) object;
+         return Objects.equal(summary, other.summary)
+               && Objects.equal(domains, other.domains);
+      } else {
+         return false;
+      }
+   }
+
+   @Override
+   public String toString() {
+      Joiner commaJoiner = Joiner.on(", ");
+      return String.format("summary=%s, domains=[%s]", summary, commaJoiner.join(getDomains()));
+   }
+
+}
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewDomain.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewDomain.java
new file mode 100644
index 0000000..0836645
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewDomain.java
@@ -0,0 +1,106 @@
+/**
+ * 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.glesys.domain;
+
+import com.google.common.base.Objects;
+
+/**
+ * @author Adam Lowe
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#email_overview" />
+ */
+public class EmailOverviewDomain {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String domain;
+      private int accounts;
+      private int aliases;
+
+      public Builder domain(String domain) {
+         this.domain = domain;
+         return this;
+      }
+
+      public Builder accounts(int accounts) {
+         this.accounts = accounts;
+         return this;
+      }
+      
+      public Builder aliases(int aliases) {
+         this.aliases = aliases;
+         return this;
+      }
+      
+      public EmailOverviewDomain build() {
+         return new EmailOverviewDomain(domain, accounts, aliases);
+      }
+      
+      public Builder fromEmailOverview(EmailOverviewDomain in) {
+         return domain(domain).accounts(in.getAccounts()).aliases(in.getAliases());
+      }
+   }
+
+   private final String domain;
+   private final int accounts;
+   private final int aliases;
+
+   public EmailOverviewDomain(String domain, int accounts, int aliases) {
+      this.domain = domain;
+      this.accounts = accounts;
+      this.aliases = aliases;
+   }
+
+   public String getDomain() {
+      return domain;
+   }
+
+   public int getAccounts() {
+      return accounts;
+   }
+
+   public int getAliases() {
+      return aliases;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(domain);
+   }
+
+   @Override
+   public boolean equals(Object object) {
+      if (object == this) {
+         return true;
+      }
+      if (object instanceof EmailOverviewDomain) {
+         EmailOverviewDomain other = (EmailOverviewDomain) object;
+         return Objects.equal(domain, other.domain);
+      } else {
+         return false;
+      }
+   }
+
+   @Override
+   public String toString() {
+      return String.format("domain=%s, accounts=%d, aliases=%d", domain, accounts, aliases);
+   }
+
+}
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewSummary.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewSummary.java
new file mode 100644
index 0000000..06bf114
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewSummary.java
@@ -0,0 +1,124 @@
+/**
+ * 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.glesys.domain;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Adam Lowe
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#email_overview" />
+ */
+public class EmailOverviewSummary {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private int accounts;
+      private int maxAccounts;
+      private int aliases;
+      private int maxAliases;
+
+      public Builder accounts(int accounts) {
+         this.accounts = accounts;
+         return this;
+      }
+      
+      public Builder maxAccounts(int maxAccounts) {
+         this.maxAccounts = maxAccounts;
+         return this;
+      }
+      
+      public Builder aliases(int aliases) {
+         this.aliases = aliases;
+         return this;
+      }
+      
+      public Builder maxAliases(int maxAliases) {
+         this.maxAliases = maxAliases;
+         return this;
+      }
+      
+      public EmailOverviewSummary build() {
+         return new EmailOverviewSummary(accounts, maxAccounts, aliases, maxAliases);
+      }
+      
+      public Builder fromEmailOverview(EmailOverviewSummary in) {
+         return accounts(in.getAccounts()).maxAccounts(in.getMaxAccounts()).aliases(in.getAliases()).maxAliases(in.getMaxAliases());
+      }
+   }
+
+   private final int accounts;
+   @SerializedName("maxaccounts")
+   private final int maxAccounts;
+   private final int aliases;
+   @SerializedName("maxaliases")
+   private final int maxAliases;
+
+   public EmailOverviewSummary(int accounts, int maxAccounts, int aliases, int maxAliases) {
+      this.accounts = accounts;
+      this.maxAccounts = maxAccounts;
+      this.aliases = aliases;
+      this.maxAliases = maxAliases;
+   }
+
+   public int getAccounts() {
+      return accounts;
+   }
+
+   public int getMaxAccounts() {
+      return maxAccounts;
+   }
+
+   public int getAliases() {
+      return aliases;
+   }
+
+   public int getMaxAliases() {
+      return maxAliases;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(accounts, maxAccounts, aliases, maxAliases);
+   }
+
+   @Override
+   public boolean equals(Object object) {
+      if (object == this) {
+         return true;
+      }
+      if (object instanceof EmailOverviewSummary) {
+         EmailOverviewSummary other = (EmailOverviewSummary) object;
+         return Objects.equal(accounts, other.accounts)
+               && Objects.equal(maxAccounts, other.maxAccounts)
+               && Objects.equal(aliases, other.aliases)
+               && Objects.equal(maxAliases, other.maxAliases);
+      } else {
+         return false;
+      }
+   }
+
+   @Override
+   public String toString() {
+      return String.format("accounts=%d, maxAccounts=%d, aliases=%d, maxAliases=%d", accounts, maxAccounts, aliases, maxAliases);
+   }
+
+}
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/IpDetails.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/IpDetails.java
index 1e4d16f..6774568 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/IpDetails.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/IpDetails.java
@@ -26,7 +26,7 @@
 import java.util.List;
 
 /**
- * Represents detailed information about an available IP address.
+ * Represents detailed information about an IP address.
  */
 public class IpDetails {
 
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerConsole.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerConsole.java
index 97a8692..bf15950 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerConsole.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerConsole.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.domain;
 
 import com.google.common.base.Objects;
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerCreated.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerCreated.java
index 3117f8f..07aa87b 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerCreated.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerCreated.java
@@ -1,21 +1,39 @@
+/**
+ * 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.glesys.domain;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.gson.annotations.SerializedName;
-import org.jclouds.javax.annotation.Nullable;
 
 /**
  * Connection information to connect to a server with VNC.
  * 
  * @author Adam Lowe
- * @see <a href="https://customer.glesys.com/api.php?a=doc#server_console" />
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#server_create" />
  */
 public class ServerCreated {
    public static Builder builder() {
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerCreatedIp.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerCreatedIp.java
index f8c2101..464355f 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerCreatedIp.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerCreatedIp.java
@@ -38,11 +38,19 @@
       protected int version;
       protected double cost;
 
-      public Builder version(int version) {
+      protected Builder version(int version) {
          this.version = version;
          return this;
       }
 
+      public Builder version4() {
+         return version(4);
+      }
+
+      public Builder version6() {
+         return version(6);
+      }
+      
       public Builder ip(String ip) {
          this.ip = ip;
          return this;
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerDetails.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerDetails.java
index 1261642..e8347bf 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerDetails.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerDetails.java
@@ -20,7 +20,6 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import com.google.common.base.Objects;
 import com.google.gson.annotations.SerializedName;
 
 /**
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerLimit.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerLimit.java
index b0faaa0..3a3a60b 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerLimit.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerLimit.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.domain;
 
 import com.google.common.base.Objects;
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerTemplate.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerTemplate.java
index 3f923ff..4115bb4 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerTemplate.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/domain/ServerTemplate.java
@@ -1,6 +1,25 @@
+/**
+ * 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.glesys.domain;
 
 import com.google.common.base.Objects;
+import com.google.common.collect.Ordering;
 import com.google.gson.annotations.SerializedName;
 
 /**
@@ -9,7 +28,7 @@
  * @author Adam Lowe
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#server_templates" />
  */
-public class ServerTemplate {
+public class ServerTemplate implements Comparable<ServerTemplate>{
 
    public static Builder builder() {
       return new Builder();
@@ -131,4 +150,9 @@
       return String.format("[name=%s, min_disk_size=%d, min_mem_size=%d, os=%s, platform=%s]",
             name, minDiskSize, minMemSize, os, platform);
    }
+
+   @Override
+   public int compareTo(ServerTemplate arg0) {
+      return Ordering.usingToString().compare(this, arg0);
+   }
 }
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ArchiveAsyncClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ArchiveAsyncClient.java
index 2576721..4098ebe 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ArchiveAsyncClient.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ArchiveAsyncClient.java
@@ -55,14 +55,14 @@
    ListenableFuture<Set<Archive>> listArchives();
 
    /**
-    * @see ArchiveClient#archiveDetails
+    * @see ArchiveClient#getArchiveDetails
     */
    @POST
    @Path("/archive/details/format/json")
    @SelectJson("details")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<ArchiveDetails> archiveDetails(@FormParam("username") String username);
+   ListenableFuture<ArchiveDetails> getArchiveDetails(@FormParam("username") String username);
 
    /**
     * @see ArchiveClient#createArchive
@@ -84,7 +84,7 @@
     */
    @POST
    @Path("/archive/resize/format/json")
-   ListenableFuture<Void> resizeArchive(@FormParam("username") String username, @FormParam("size")int size);
+   ListenableFuture<Void> resizeArchive(@FormParam("username") String username, @FormParam("size") int size);
    /**
     * @see ArchiveClient#changeArchivePassword
     */
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ArchiveClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ArchiveClient.java
index 039fb52..5180763 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ArchiveClient.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ArchiveClient.java
@@ -48,7 +48,7 @@
     * @param username the username associated with the archive
     * @return the archive information or null if not found
     */
-   ArchiveDetails archiveDetails(String username);
+   ArchiveDetails getArchiveDetails(String username);
 
    /**
     * Create a new backup volume.
@@ -73,7 +73,7 @@
     * Then delete the old volume.
     *
     * @param username the username associated with the archive
-    * @param size     the new size required in GB
+    * @param size     the new size required, see #getArchiveAllowedArguments for valid values
     */
    void resizeArchive(String username, int size);
 
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/EmailAsyncClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/EmailAsyncClient.java
new file mode 100644
index 0000000..7d88a81
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/EmailAsyncClient.java
@@ -0,0 +1,107 @@
+/**
+ * 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.glesys.features;
+
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.glesys.domain.Email;
+import org.jclouds.glesys.domain.EmailOverview;
+import org.jclouds.glesys.options.EmailCreateOptions;
+import org.jclouds.glesys.options.EmailEditOptions;
+import org.jclouds.http.filters.BasicAuthentication;
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides asynchronous access to E-Mail data via the Glesys REST API.
+ * <p/>
+ *
+ * @author Adam Lowe
+ * @see org.jclouds.glesys.features.EmailClient
+ * @see <a href="https://customer.glesys.com/api.php" />
+ */
+@RequestFilters(BasicAuthentication.class)
+public interface EmailAsyncClient {
+
+   /**
+    * @see org.jclouds.glesys.features.EmailClient#emailOverview
+    */
+   @POST
+   @Path("/email/overview/format/json")
+   @SelectJson("response")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   ListenableFuture<EmailOverview> getEmailOverview();
+
+   /**
+    * @see org.jclouds.glesys.features.EmailClient#listAccounts
+    */
+   @POST
+   @Path("/email/list/format/json")
+   @SelectJson("emailaccounts")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   ListenableFuture<Set<Email>> listAccounts(@FormParam("domain") String domain);
+
+   /**
+    * @see org.jclouds.glesys.features.EmailClient#createAccount
+    */
+   @POST
+   @Path("/email/createaccount/format/json")
+   ListenableFuture<Void> createAccount(@FormParam("emailaccount") String accountAddress, @FormParam("password") String password, EmailCreateOptions... options);
+
+   /**
+    * @see org.jclouds.glesys.features.EmailClient#createAlias
+    */
+   @POST
+   @Path("/email/createalias/format/json")
+   ListenableFuture<Void> createAlias(@FormParam("emailalias") String aliasAddress, @FormParam("goto") String toEmailAddress);
+
+   /**
+    * @see org.jclouds.glesys.features.EmailClient#editAccount
+    */
+   @POST
+   @Path("/email/editaccount/format/json")
+   ListenableFuture<Void> editAccount(@FormParam("emailaccount") String accountAddress, EmailEditOptions... options);
+
+   /**
+    * @see org.jclouds.glesys.features.EmailClient#editAlias
+    */
+   @POST
+   @Path("/email/editalias/format/json")
+   ListenableFuture<Void> editAlias(@FormParam("emailalias") String aliasAddress, @FormParam("goto") String toEmailAddress);
+
+   /**
+    * @see org.jclouds.glesys.features.EmailClient#delete
+    */
+   @POST
+   @Path("/email/delete/format/json")
+   ListenableFuture<Void> delete(@FormParam("email") String accountAddress);
+
+}
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/EmailClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/EmailClient.java
new file mode 100644
index 0000000..b2304e4
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/EmailClient.java
@@ -0,0 +1,64 @@
+/**
+ * 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.glesys.features;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.glesys.domain.Email;
+import org.jclouds.glesys.domain.EmailOverview;
+import org.jclouds.glesys.options.EmailCreateOptions;
+import org.jclouds.glesys.options.EmailEditOptions;
+
+/**
+ * Provides synchronous access to E-Mail requests.
+ * <p/>
+ *
+ * @author Adam Lowe
+ * @see org.jclouds.glesys.features.DomainAsyncClient
+ * @see <a href="https://customer.glesys.com/api.php" />
+ */
+@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
+public interface EmailClient {
+
+   /**
+    * Get a summary of e-mail accounts associated with this Glesys account
+    *
+    * @return the relevant summary data
+    */
+   EmailOverview getEmailOverview();
+
+   /**
+    * 
+    * @return
+    */
+   Set<Email> listAccounts(String domain);
+
+   void createAccount(String accountAddress, String password, EmailCreateOptions... options);
+
+   void createAlias(String aliasAddress, String toEmailAddress);
+
+   void editAccount(String accountAddress, EmailEditOptions... options);
+   
+   void editAlias(String aliasAddress, String toEmailAddress);
+
+   void delete(String accountAddress);
+
+}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/IpAsyncClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/IpAsyncClient.java
index 6f67d23..ab8f2c3 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/IpAsyncClient.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/IpAsyncClient.java
@@ -25,11 +25,9 @@
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.SelectJson;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
+import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import java.util.Set;
 
@@ -37,7 +35,8 @@
  * Provides asynchronous access to IP Addresses via their REST API.
  * <p/>
  *
- * @author Adrian Cole
+ * @author Adrian Cole, Mattias Holmqvist
+ *
  * @see ServerClient
  * @see <a href="https://customer.glesys.com/api.php" />
  */
@@ -46,6 +45,41 @@
 
 
    /**
+    * @see IpClient#take
+    */
+   @POST
+   @Path("/ip/take/format/json")
+   ListenableFuture<Void> take(@FormParam("ipaddress") String ipAddress);
+
+
+   /**
+    * @see IpClient#release
+    */
+   @POST
+   @Path("/ip/release/format/json")
+   ListenableFuture<Void> release(@FormParam("ipaddress") String ipAddress);
+
+   /**
+    * @see IpClient#add
+    */
+   @POST
+   @Path("/ip/add/format/json")
+   ListenableFuture<Void> addIpToServer(@FormParam("ipaddress") String ipAddress,
+                                        @FormParam("serverid") String serverId);
+
+
+   /**
+    * @see IpClient#remove
+    *
+    * TODO: add optional release_ip parameter
+    */
+   @POST
+   @Path("/ip/remove/format/json")
+   ListenableFuture<Void> removeIpFromServer(@FormParam("ipaddress") String ipAddress,
+                                 @FormParam("serverid") String serverId);
+
+
+   /**
     * @see IpClient#listFree
     */
    @GET
@@ -64,7 +98,7 @@
    @Path("/ip/details/ipaddress/{ipaddress}/format/json")
    @Consumes(MediaType.APPLICATION_JSON)
    @SelectJson("details")
-   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
    ListenableFuture<IpDetails> getIpDetails(@PathParam("ipaddress") String ipAddress);
 
 }
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/IpClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/IpClient.java
index 394c32d..0ada33d 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/IpClient.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/IpClient.java
@@ -28,32 +28,67 @@
  * Provides synchronous access to IP Addresses.
  * <p/>
  *
- * @author Adrian Cole
+ * @author Adrian Cole, Mattias Holmqvist
  * @see IpAsyncClient
  * @see <a href="https://customer.glesys.com/api.php" />
  */
 @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
 public interface IpClient {
 
+    /**
+     * Take a free IP address and add it to this account. You can list free IP addresses with the function listFree().
+     * Once your free IP on this account you can add it to a server with the add() function.
+     *
+     * @param ipAddress
+     */
+    void take(String ipAddress);
 
-   /**
-    * Get a set of all IP addresses that are available and not used on any account or server.
-    *
-    * @param ipversion  "4" or "6", for IPV4 or IPV6, respectively
-    * @param datacenter the datacenter
-    * @param platform   the platform
-    * @return a set of free IP addresses
-    */
-   Set<String> listFree(String ipversion, String datacenter, String platform);
+    /**
+     * Return an unused IP address to the pool of free ips. If the IP address is allocated to a server,
+     * it must first be removed by calling remove(ipAddress) before it can be released.
+     *
+     * @param ipAddress the IP address to be released
+     */
+    void release(String ipAddress);
 
-   /**
-    * Get details about the given IP address such as gateway and netmask. Different details are available
-    * on different platforms.
-    *
-    * @param ipAddress the ip address
-    * @return details about the given IP saddress
-    */
-   IpDetails getIpDetails(String ipAddress);
+    /**
+     * Add an IP address to an server. The IP has to be free, but reserved to this account. You are able to list such addresses
+     * with listOwn() and reserve an address for this account by using take(). To find free ips you can use ip/listfree
+     * ip to an Xen-server you have to configure the server yourself, unless the ip was added during the c
+     * server (server/create). You can get detailed information such as gateway and netmask using the ip
+     *
+     * @param ipAddress the IP address to remove
+     * @param serverId  the server to add the IP address to
+     */
+    void addIpToServer(String ipAddress, String serverId);
 
+    /**
+     * Remove an IP address from a server. This does not release it back to GleSYS pool of free ips. The address will be
+     * kept on the account so that you can use it for other servers or the same server at a later time. To completely remove
+     * the IP address from this account, use the function release().
+     *
+     * @param ipAddress the IP address to remove
+     * @param serverId  the server to remove the IP address from
+     */
+    void removeIpFromServer(String ipAddress, String serverId);
+
+    /**
+     * Get a set of all IP addresses that are available and not used on any account or server.
+     *
+     * @param ipversion  "4" or "6", for IPV4 or IPV6, respectively
+     * @param datacenter the datacenter
+     * @param platform   the platform
+     * @return a set of free IP addresses
+     */
+    Set<String> listFree(String ipversion, String datacenter, String platform);
+
+    /**
+     * Get details about the given IP address such as gateway and netmask. Different details are available
+     * on different platforms.
+     *
+     * @param ipAddress the ip address
+     * @return details about the given IP address
+     */
+    IpDetails getIpDetails(String ipAddress);
 
 }
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ServerAsyncClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ServerAsyncClient.java
index df12c4c..02c7fe5 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ServerAsyncClient.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ServerAsyncClient.java
@@ -18,19 +18,41 @@
  */
 package org.jclouds.glesys.features;
 
-import com.google.common.util.concurrent.ListenableFuture;
-import org.jclouds.glesys.domain.*;
-import org.jclouds.glesys.options.*;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.glesys.domain.Server;
+import org.jclouds.glesys.domain.ServerAllowedArguments;
+import org.jclouds.glesys.domain.ServerConsole;
+import org.jclouds.glesys.domain.ServerCreated;
+import org.jclouds.glesys.domain.ServerDetails;
+import org.jclouds.glesys.domain.ServerLimit;
+import org.jclouds.glesys.domain.ServerStatus;
+import org.jclouds.glesys.domain.ServerTemplate;
+import org.jclouds.glesys.functions.ParseServerTemplatesFromHttpResponse;
+import org.jclouds.glesys.options.ServerCloneOptions;
+import org.jclouds.glesys.options.ServerCreateOptions;
+import org.jclouds.glesys.options.ServerDestroyOptions;
+import org.jclouds.glesys.options.ServerEditOptions;
+import org.jclouds.glesys.options.ServerStatusOptions;
+import org.jclouds.glesys.options.ServerStopOptions;
 import org.jclouds.http.filters.BasicAuthentication;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SelectJson;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
 import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 
-import javax.ws.rs.*;
-import javax.ws.rs.core.MediaType;
-import java.util.*;
+import com.google.common.util.concurrent.ListenableFuture;
 
 /**
  * Provides asynchronous access to Server via their REST API.
@@ -90,7 +112,7 @@
     */
    @POST
    @Path("/server/console/format/json")
-   @SelectJson("server")
+   @SelectJson("remote")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
    ListenableFuture<ServerConsole> getServerConsole(@FormParam("serverid") String id);
@@ -110,9 +132,9 @@
     */
    @GET
    @Path("/server/templates/format/json")
-   @SelectJson("templates")
+   @ResponseParser(ParseServerTemplatesFromHttpResponse.class)
    @Consumes(MediaType.APPLICATION_JSON)
-   ListenableFuture<Map<String, Set<ServerTemplate>>> getTemplates();
+   ListenableFuture<Set<ServerTemplate>> getTemplates();
    
    /**
     * @see ServerClient#stopServer
@@ -183,7 +205,7 @@
     */
    @POST
    @Path("/server/destroy/format/json")
-   ListenableFuture<Void> destroyServer(@FormParam("serverid") String id, @FormParam("keepip") int keepIp);
+   ListenableFuture<Void> destroyServer(@FormParam("serverid") String id, ServerDestroyOptions keepIp);
 
    /**
     * @see ServerClient#resetPassword
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ServerClient.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ServerClient.java
index 554af8d..8d0b966 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ServerClient.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/features/ServerClient.java
@@ -18,16 +18,28 @@
  */
 package org.jclouds.glesys.features;
 
-import org.jclouds.concurrent.Timeout;
-import org.jclouds.glesys.domain.*;
-import org.jclouds.glesys.options.*;
-import org.jclouds.javax.annotation.Nullable;
-
-import javax.ws.rs.FormParam;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
+import javax.ws.rs.FormParam;
+
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.glesys.domain.Server;
+import org.jclouds.glesys.domain.ServerAllowedArguments;
+import org.jclouds.glesys.domain.ServerConsole;
+import org.jclouds.glesys.domain.ServerCreated;
+import org.jclouds.glesys.domain.ServerDetails;
+import org.jclouds.glesys.domain.ServerLimit;
+import org.jclouds.glesys.domain.ServerStatus;
+import org.jclouds.glesys.domain.ServerTemplate;
+import org.jclouds.glesys.options.ServerCloneOptions;
+import org.jclouds.glesys.options.ServerCreateOptions;
+import org.jclouds.glesys.options.ServerDestroyOptions;
+import org.jclouds.glesys.options.ServerEditOptions;
+import org.jclouds.glesys.options.ServerStatusOptions;
+import org.jclouds.glesys.options.ServerStopOptions;
+
 /**
  * Provides synchronous access to Server.
  * <p/>
@@ -169,9 +181,9 @@
     * Destroy a server
     *
     * @param id     the id of the server
-    * @param keepIp if 1 the servers ip will be retained for use in your Glesys account
+    * @param keepIp if ServerDestroyOptions.keepIp(true) the servers ip will be retained for use in your GleSYS account
     */
-   void destroyServer(String id, int keepIp);
+   void destroyServer(String id, ServerDestroyOptions keepIp);
 
    /**
     * Reset the root password of a server
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/ParseServerTemplatesFromHttpResponse.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/ParseServerTemplatesFromHttpResponse.java
new file mode 100644
index 0000000..58e6481
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/ParseServerTemplatesFromHttpResponse.java
@@ -0,0 +1,59 @@
+/**
+ * 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.glesys.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Singleton;
+
+import org.jclouds.glesys.domain.ServerTemplate;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseFirstJsonValueNamed;
+import org.jclouds.json.internal.GsonWrapper;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class ParseServerTemplatesFromHttpResponse implements Function<HttpResponse, Set<ServerTemplate>> {
+   private final ParseFirstJsonValueNamed<Map<String, Set<ServerTemplate>>> parser;
+
+   @Inject
+   public ParseServerTemplatesFromHttpResponse(GsonWrapper gsonWrapper) {
+      this.parser = new ParseFirstJsonValueNamed<Map<String, Set<ServerTemplate>>>(checkNotNull(gsonWrapper,
+               "gsonWrapper"), new TypeLiteral<Map<String, Set<ServerTemplate>>>() {
+      }, "templates");
+   }
+
+   public Set<ServerTemplate> apply(HttpResponse response) {
+      checkNotNull(response, "response");
+      Map<String, Set<ServerTemplate>> toParse = parser.apply(response);
+      checkNotNull(toParse, "parsed result from %s", response);
+      return ImmutableSet.copyOf(Iterables.concat(toParse.values()));
+   }
+}
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/CustomDeserializers.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/CustomDeserializers.java
deleted file mode 100644
index dd4b477..0000000
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/CustomDeserializers.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.jclouds.glesys.functions.internal;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import org.jclouds.glesys.domain.ServerState;
-import org.jclouds.glesys.domain.ServerUptime;
-
-import java.lang.reflect.Type;
-
-/**
- * @author Adam Lowe
- */
-public class CustomDeserializers {
-
-   public static class ServerStateAdapter implements JsonDeserializer<ServerState> {
-      @Override
-      public ServerState deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
-            throws JsonParseException {
-         String toParse = jsonElement.getAsJsonPrimitive().getAsString();
-         return ServerState.fromValue(toParse);
-      }
-   }
-
-   public static class ServerUptimeAdaptor implements JsonDeserializer<ServerUptime> {
-      @Override
-      public ServerUptime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context)
-            throws JsonParseException {
-         String toParse = jsonElement.getAsJsonPrimitive().getAsString();
-         return ServerUptime.fromValue(toParse);
-      }
-   }
-}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/GleSYSTypeAdapters.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/GleSYSTypeAdapters.java
new file mode 100644
index 0000000..b1e26bb
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/GleSYSTypeAdapters.java
@@ -0,0 +1,58 @@
+/**
+ * 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.glesys.functions.internal;
+
+import java.io.IOException;
+
+import org.jclouds.glesys.domain.ServerState;
+import org.jclouds.glesys.domain.ServerUptime;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * @author Adam Lowe
+ */
+public class GleSYSTypeAdapters {
+
+   public static class ServerStateAdapter extends TypeAdapter<ServerState> {
+      @Override
+      public void write(JsonWriter writer, ServerState value) throws IOException {
+         writer.value(value.value());
+      }
+
+      @Override
+      public ServerState read(JsonReader reader) throws IOException {
+         return ServerState.fromValue(reader.nextString());
+      }
+   }
+
+   public static class ServerUptimeAdapter extends TypeAdapter<ServerUptime> {
+      @Override
+      public void write(JsonWriter writer, ServerUptime value) throws IOException {
+         writer.value(value.toString());
+      }
+
+      @Override
+      public ServerUptime read(JsonReader reader) throws IOException {
+         return ServerUptime.fromValue(reader.nextString());
+      }
+   }
+}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/GlesysDateAdapter.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/GlesysDateAdapter.java
index 6568b65..6b7bb1a 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/GlesysDateAdapter.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/functions/internal/GlesysDateAdapter.java
@@ -1,39 +1,60 @@
+/**
+ * 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.glesys.functions.internal;
 
-import com.google.gson.*;
-import org.jclouds.json.config.GsonModule;
-
-import javax.inject.Singleton;
-import java.lang.reflect.Type;
+import java.io.IOException;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 
+import javax.inject.Singleton;
+
+import org.jclouds.json.config.GsonModule;
+
+import com.google.common.base.Throwables;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
 /**
  * Parser for Glesys Date formats
  *
  * @author Adam Lowe
  */
 @Singleton
-public class GlesysDateAdapter implements GsonModule.DateAdapter {
+public class GlesysDateAdapter extends GsonModule.DateAdapter {
    private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
-   public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
+   public void write(JsonWriter writer, Date value) throws IOException {
       synchronized (dateFormat) {
-         return new JsonPrimitive(dateFormat.format(src));
+         writer.value(dateFormat.format(value));
       }
    }
 
-   public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
-         throws JsonParseException {
-      String toParse = json.getAsJsonPrimitive().getAsString();
+   public Date read(JsonReader reader) throws IOException {
+      String toParse = reader.nextString();
       try {
          synchronized (dateFormat) {
             return dateFormat.parse(toParse);
          }
       } catch (ParseException e) {
-         throw new RuntimeException(e);
+         throw Throwables.propagate(e);
       }
    }
 }
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainOptions.java
index a7e8deb..6bc47aa 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainOptions.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainOptions.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.options;
 
 import org.jclouds.http.options.BaseHttpRequestOptions;
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainRecordAddOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainRecordAddOptions.java
index 8ab0236..fc304b8 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainRecordAddOptions.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainRecordAddOptions.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.options;
 
 import org.jclouds.http.options.BaseHttpRequestOptions;
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainRecordModifyOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainRecordModifyOptions.java
index 905219f..50e46d9 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainRecordModifyOptions.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/DomainRecordModifyOptions.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.options;
 
 /**
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/EmailCreateOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/EmailCreateOptions.java
new file mode 100644
index 0000000..b253f6c
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/EmailCreateOptions.java
@@ -0,0 +1,89 @@
+/**
+ * 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.glesys.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * @author Adam Lowe
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#email_createaccount" />
+ */
+public class EmailCreateOptions extends BaseHttpRequestOptions {
+   public static class Builder {
+      /**
+       * @see EmailCreateOptions#antispamLevel
+       */
+      public static EmailCreateOptions antispamLevel(int antispamLevel) {
+         return new EmailCreateOptions().antispamLevel(antispamLevel);
+      }
+
+      /**
+       * @see EmailCreateOptions#antiVirus
+       */
+      public static EmailCreateOptions antiVirus(boolean antiVirus) {
+         return new EmailCreateOptions().antiVirus(antiVirus);
+      }
+
+      /**
+       * @see EmailCreateOptions#autorespond
+       */
+      public static EmailCreateOptions autorespond(boolean autorespond) {
+         return new EmailCreateOptions().autorespond(autorespond);
+      }
+
+      /**
+       * @see EmailCreateOptions#autorespondSaveEmail
+       */
+      public static EmailCreateOptions autorespondSaveEmail(boolean autorespondSaveEmail) {
+         return new EmailCreateOptions().autorespondSaveEmail(autorespondSaveEmail);
+      }
+
+      /**
+       * @see EmailCreateOptions#autorespondMessage
+       */
+      public static EmailCreateOptions autorespondMessage(boolean autorespondMessage) {
+         return new EmailCreateOptions().autorespondMessage(autorespondMessage);
+      }
+   }
+
+   public EmailCreateOptions antispamLevel(int antispamLevel) {
+      formParameters.put("antispamlevel", Integer.toString(antispamLevel));
+      return this;
+   }
+
+   public EmailCreateOptions antiVirus(boolean antiVirus) {
+      formParameters.put("antivirus", Integer.toString(antiVirus ? 1 : 0));
+      return this;
+   }
+
+   public EmailCreateOptions autorespond(boolean autorespond) {
+      formParameters.put("autorespond", Integer.toString(autorespond ? 1 : 0));
+      return this;
+   }
+
+   public EmailCreateOptions autorespondSaveEmail(boolean autorespondSaveEmail) {
+      formParameters.put("autorespondsaveemail", Integer.toString(autorespondSaveEmail ? 1 : 0));
+      return this;
+   }
+
+   public EmailCreateOptions autorespondMessage(boolean autorespondMessage) {
+      formParameters.put("autorespondmessage", Integer.toString(autorespondMessage ? 1 : 0));
+      return this;
+   }
+}
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/EmailEditOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/EmailEditOptions.java
new file mode 100644
index 0000000..97f0e90
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/EmailEditOptions.java
@@ -0,0 +1,58 @@
+/**
+ * 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.glesys.options;
+
+
+/**
+ * @author Adam Lowe
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#email_editaccount" />
+ */
+public class EmailEditOptions extends EmailCreateOptions {
+
+   public static class Builder {
+      public static EmailEditOptions antispamLevel(int antispamLevel) {
+         return EmailEditOptions.class.cast(new EmailEditOptions().antispamLevel(antispamLevel));
+      }
+
+      public static EmailEditOptions antiVirus(boolean antiVirus) {
+         return EmailEditOptions.class.cast(new EmailEditOptions().antiVirus(antiVirus));
+      }
+
+      public static EmailEditOptions autorespond(boolean autorespond) {
+         return EmailEditOptions.class.cast(new EmailEditOptions().autorespond(autorespond));
+      }
+
+      public static EmailEditOptions autorespondSaveEmail(boolean autorespondSaveEmail) {
+         return EmailEditOptions.class.cast(new EmailEditOptions().autorespondSaveEmail(autorespondSaveEmail));
+      }
+
+      public static EmailEditOptions autorespondMessage(boolean autorespondMessage) {
+         return EmailEditOptions.class.cast(new EmailEditOptions().autorespondMessage(autorespondMessage));
+      }
+
+      public static EmailEditOptions autorespondMessage(String password) {
+         return new EmailEditOptions().password(password);
+      }
+   }
+
+   public EmailEditOptions password(String password) {
+      formParameters.put("password", password);
+      return this;
+   }
+}
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerCloneOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerCloneOptions.java
index 25d877e..388a49d 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerCloneOptions.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerCloneOptions.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.options;
 
 /**
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerCreateOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerCreateOptions.java
index 858f393..aeedd44 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerCreateOptions.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerCreateOptions.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.options;
 
 import org.jclouds.http.options.BaseHttpRequestOptions;
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerDestroyOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerDestroyOptions.java
new file mode 100644
index 0000000..62099dd
--- /dev/null
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerDestroyOptions.java
@@ -0,0 +1,53 @@
+/**
+ * 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.glesys.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * @author Adam Lowe
+ */
+public class ServerDestroyOptions extends BaseHttpRequestOptions {
+   public static class Builder {
+      /**
+       * Discard the server's ip on destroy
+       */
+      public static ServerDestroyOptions keepIp() {
+         return new ServerDestroyOptions().keepIp(true);
+      }
+
+      /**
+       * Discard the server's ip on destroy
+       */
+      public static ServerDestroyOptions discardIp() {
+         return new ServerDestroyOptions().keepIp(false);
+      }
+
+   }
+
+   /**
+    * Determines whether to keep the server's ip attached to your account when destroying a server
+    *
+    * @param keepIp if true, keep the ip address
+    */
+   public ServerDestroyOptions keepIp(boolean keepIp) {
+      formParameters.put("keepip", Integer.toString(keepIp ? 1 : 0));
+      return this;
+   }
+}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerEditOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerEditOptions.java
index 3518fbe..b45070b 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerEditOptions.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerEditOptions.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.options;
 
 import org.jclouds.http.options.BaseHttpRequestOptions;
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerStatusOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerStatusOptions.java
index c923e04..bc13482 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerStatusOptions.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerStatusOptions.java
@@ -1,6 +1,23 @@
+/**
+ * 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.glesys.options;
 
-import com.google.common.collect.Iterables;
 import org.jclouds.http.options.BaseHttpRequestOptions;
 
 /**
diff --git a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerStopOptions.java b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerStopOptions.java
index b8c337c..1e81f49 100644
--- a/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerStopOptions.java
+++ b/sandbox-providers/glesys/src/main/java/org/jclouds/glesys/options/ServerStopOptions.java
@@ -1,3 +1,21 @@
+/**
+ * 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.glesys.options;
 
 /**
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ArchiveAsyncClientTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ArchiveAsyncClientTest.java
index 6c6c7bd..124f4ec 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ArchiveAsyncClientTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ArchiveAsyncClientTest.java
@@ -18,15 +18,15 @@
  */
 package org.jclouds.glesys.features;
 
-import com.google.inject.TypeLiteral;
+import java.util.Map;
+
 import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
 import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
 import org.testng.annotations.Test;
 
-import javax.ws.rs.FormParam;
-import java.util.Map;
+import com.google.inject.TypeLiteral;
 
 /**
  * Tests annotation parsing of {@code ArchiveAsyncClient}
@@ -47,7 +47,7 @@
    }
    
    public void testArchiveDetails() throws Exception {
-      testMethod("archiveDetails", "details", "POST", true, ReturnNullOnNotFoundOr404.class, userName);
+      testMethod("getArchiveDetails", "details", "POST", true, ReturnNullOnNotFoundOr404.class, userName);
    }
    
    public void testCreateArchive() throws Exception {
@@ -61,7 +61,7 @@
 
    public void testResizeArchive() throws Exception {
       testMethod("resizeArchive", "resize", "POST", false, MapHttp4xxCodesToExceptions.class, userName,
-            newEntry("size", 5));
+            newEntry("size", "5 GB"));
    }
    
    public void testChangeArchivePassword() throws Exception {
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientLiveTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientLiveTest.java
index 0931ba6..31b6299 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientLiveTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientLiveTest.java
@@ -18,28 +18,27 @@
  */
 package org.jclouds.glesys.features;
 
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import org.jclouds.glesys.domain.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.glesys.domain.ArchiveAllowedArguments;
+import org.jclouds.glesys.domain.ArchiveDetails;
 import org.jclouds.predicates.RetryablePredicate;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import static com.google.common.base.Predicates.equalTo;
-import static org.testng.Assert.*;
+import com.google.common.base.Predicate;
 
 /**
  * Tests behavior of {@code ArchiveClient}
  *
  * @author Adam Lowe
  */
-@Test(groups = "live", testName = "ArchiveClientLiveTest")
+@Test(groups = "live", testName = "ArchiveClientLiveTest", singleThreaded = true)
 public class ArchiveClientLiveTest extends BaseGleSYSClientLiveTest {
 
    @BeforeGroups(groups = {"live"})
@@ -97,7 +96,7 @@
 
    @Test(dependsOnMethods = "testCreateArchive")
    public void testArchiveDetails() throws Exception {
-      ArchiveDetails details = client.archiveDetails(archiveUser);
+      ArchiveDetails details = client.getArchiveDetails(archiveUser);
       assertEquals(details.getUsername(), archiveUser);
       assertNotNull(details.getFreeSize());
       assertNotNull(details.getTotalSize());
@@ -109,15 +108,14 @@
       // TODO assert something useful!
    }
 
-   // TODO enable this once issue is resolved
-   @Test(enabled=false, dependsOnMethods = "testCreateArchive")
+   @Test(dependsOnMethods = "testCreateArchive")
    public void testResizeArchive() throws Exception {
-      client.resizeArchive(archiveUser, 30);
+      client.resizeArchive(archiveUser, 20);
 
       assertTrue(new RetryablePredicate<String>(
             new Predicate<String>() {
                public boolean apply(String value){
-                  return client.archiveDetails(archiveUser) != null && value.equals(client.archiveDetails(archiveUser).getTotalSize());
+                  return client.getArchiveDetails(archiveUser) != null && value.equals(client.getArchiveDetails(archiveUser).getTotalSize());
                }
             }, 30, 1, TimeUnit.SECONDS).apply("20 GB"));
    }
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/BaseGleSYSAsyncClientTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/BaseGleSYSAsyncClientTest.java
index a481233..26e799a 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/BaseGleSYSAsyncClientTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/BaseGleSYSAsyncClientTest.java
@@ -18,9 +18,16 @@
  */
 package org.jclouds.glesys.features;
 
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Maps;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
 import org.jclouds.glesys.GleSYSAsyncClient;
 import org.jclouds.glesys.GleSYSClient;
 import org.jclouds.http.HttpRequest;
@@ -30,21 +37,18 @@
 import org.jclouds.rest.RestClientTest;
 import org.jclouds.rest.RestContextFactory;
 import org.jclouds.rest.RestContextSpec;
+import org.jclouds.util.Strings2;
 
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import static org.testng.Assert.*;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Maps;
 
 /**
  * @author Adrian Cole
  * @author Adam Lowe
  */
 public abstract class BaseGleSYSAsyncClientTest<T> extends RestClientTest<T> {
-   protected Class asyncClientClass;
+   protected Class<T> asyncClientClass;
    protected String remoteServicePrefix;
 
    @Override
@@ -58,7 +62,8 @@
       Properties props = new Properties();
       return new RestContextFactory().createContextSpec("glesys", "username", "apiKey", props);
    }
-
+   
+   @Deprecated
    protected Map.Entry<String, String> newEntry(String key, Object value) {
       return Maps.immutableEntry(key, value.toString());
    }
@@ -66,8 +71,6 @@
    /**
     * Test that a method call is annotated correctly.
     * <p/>
-    * TODO de-code ampersands and spaces in args properly
-    *
     * @param localMethod     the method to call in asyncClientClass
     * @param remoteCall      the name of the expected call on the remote server
     * @param httpMethod      "GET" or "POST"
@@ -75,7 +78,17 @@
     * @param exceptionParser the class of exception handler expected
     * @param args            either Map.Entry or BaseHttpRequestOptions that make up the arguments to the method
     */
-   protected void testMethod(String localMethod, String remoteCall, String httpMethod, boolean expectResponse, Class exceptionParser, Object... args) throws Exception {
+   //TODO: kill this and related logic and transition to BaseRestClientExpectTest<GleSYSClient>
+   @Deprecated
+   protected void testMethod(String localMethod, String remoteCall, String httpMethod, boolean expectResponse,
+            Class<?> exceptionParser, Object... args) throws Exception {
+      testMethod(localMethod, remoteCall, httpMethod, expectResponse, ParseFirstJsonValueNamed.class, exceptionParser,
+               args);
+   }
+   
+   @Deprecated
+   @SuppressWarnings("unchecked")
+   protected void testMethod(String localMethod, String remoteCall, String httpMethod, boolean expectResponse, Class<?> responseParser, Class<?> exceptionParser, Object... args) throws Exception {
       List<String> argStrings = new ArrayList<String>();
       List<Object> argValues = new ArrayList<Object>();
 
@@ -87,7 +100,7 @@
             argValues.add(arg);
          } else {
             Map.Entry<String, String> entry = (Map.Entry<String, String>) arg;
-            argStrings.add(entry.getKey() + "=" + entry.getValue());
+            argStrings.add(entry.getKey() + "=" + Strings2.urlEncode(entry.getValue()));
             argValues.add(entry.getValue());
          }
       }
@@ -108,7 +121,7 @@
 
       if (expectResponse) {
          assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\n");
-         assertResponseParserClassEquals(method, httpRequest, ParseFirstJsonValueNamed.class);
+         assertResponseParserClassEquals(method, httpRequest, responseParser);
       }
 
       if (argStrings.isEmpty()) {
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/BaseGleSYSClientLiveTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/BaseGleSYSClientLiveTest.java
index 206ca54..4e607f4 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/BaseGleSYSClientLiveTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/BaseGleSYSClientLiveTest.java
@@ -19,10 +19,17 @@
 package org.jclouds.glesys.features;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.*;
 
+import com.google.common.base.Predicate;
 import org.jclouds.glesys.GleSYSAsyncClient;
 import org.jclouds.glesys.GleSYSClient;
+import org.jclouds.glesys.domain.ServerCreated;
+import org.jclouds.glesys.domain.ServerState;
+import org.jclouds.glesys.domain.ServerStatus;
+import org.jclouds.glesys.options.ServerStatusOptions;
 import org.jclouds.logging.log4j.config.Log4JLoggingModule;
+import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.rest.RestContext;
 import org.jclouds.rest.RestContextFactory;
 import org.jclouds.sshj.config.SshjSshClientModule;
@@ -33,10 +40,12 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Module;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Tests behavior of {@code GleSYSClient}
  * 
- * @author Adrian Cole
+ * @author Adrian Cole, Adam Lowe
  */
 @Test(groups = "live")
 public class BaseGleSYSClientLiveTest {
@@ -45,18 +54,67 @@
 
    @BeforeGroups(groups = { "live" })
    public void setupClient() {
-      String identity = checkNotNull(System.getProperty("test.glesys.identity"), "test.glesys.identity");
-      String credential = checkNotNull(System.getProperty("test.glesys.credential"), "test.glesys.credential");
-
-      context = new RestContextFactory().createContext("glesys", identity, credential,
-            ImmutableSet.<Module> of(new Log4JLoggingModule(), new SshjSshClientModule()));
-
+      if (context == null) {
+         String identity = checkNotNull(System.getProperty("test.glesys.identity"), "test.glesys.identity");
+         String credential = checkNotNull(System.getProperty("test.glesys.credential"), "test.glesys.credential");
+   
+         context = new RestContextFactory().createContext("glesys", identity, credential,
+               ImmutableSet.<Module> of(new Log4JLoggingModule(), new SshjSshClientModule()));
+      }
    }
 
    @AfterGroups(groups = "live")
    protected void tearDown() {
-      if (context != null)
+      if (context != null) {
          context.close();
+         context = null;
+      }
    }
 
+   protected void createDomain(String domain) {
+      final DomainClient client = context.getApi().getDomainClient();
+      int before = client.listDomains().size();
+      client.addDomain(domain);
+      RetryablePredicate<Integer> result = new RetryablePredicate<Integer>(
+            new Predicate<Integer>() {
+               public boolean apply(Integer value) {
+                  return client.listDomains().size() == value;
+               }
+            }, 30, 1, TimeUnit.SECONDS);
+
+      assertTrue(result.apply(before + 1));
+   }
+
+
+   protected ServerStatusChecker createServer(String hostName) {
+      ServerClient client = context.getApi().getServerClient();
+      ServerCreated testServer = client.createServer("Falkenberg", "OpenVZ", hostName, "Ubuntu 10.04 LTS 32-bit", 5, 512, 1, "password", 50);
+
+      assertNotNull(testServer.getId());
+      assertEquals(testServer.getHostname(), hostName);
+      assertFalse(testServer.getIps().isEmpty());
+
+      ServerStatusChecker runningServerCounter = new ServerStatusChecker(client, testServer.getId(), 180, 10, TimeUnit.SECONDS);
+
+      assertTrue(runningServerCounter.apply(ServerState.RUNNING));
+      return runningServerCounter;
+   }
+
+   public static class ServerStatusChecker extends RetryablePredicate<ServerState> {
+      private final String serverId;
+      public String getServerId() {
+         return serverId;
+      }
+      public ServerStatusChecker(final ServerClient client, final String serverId, long maxWait, long period, TimeUnit unit) {
+         super(new Predicate<ServerState>() {
+
+            public boolean apply(ServerState value) {
+               ServerStatus status = client.getServerStatus(serverId, ServerStatusOptions.Builder.state());
+               return status.getState() == value;
+            }
+
+         }, maxWait, period, unit);
+         this.serverId = serverId;
+      }
+   }
 }
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/DomainAsyncClientTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/DomainAsyncClientTest.java
index 5cb73e7..e945854 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/DomainAsyncClientTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/DomainAsyncClientTest.java
@@ -18,15 +18,15 @@
  */
 package org.jclouds.glesys.features;
 
-import com.google.inject.TypeLiteral;
+import java.util.Map;
+
 import org.jclouds.glesys.options.DomainOptions;
 import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
-import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
 import org.testng.annotations.Test;
 
-import java.util.Map;
+import com.google.inject.TypeLiteral;
 
 /**
  * Tests annotation parsing of {@code DomainAsyncClient}
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/DomainClientLiveTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/DomainClientLiveTest.java
index 14459ae..4ab2e64 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/DomainClientLiveTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/DomainClientLiveTest.java
@@ -18,25 +18,27 @@
  */
 package org.jclouds.glesys.features;
 
-import com.google.common.base.Predicate;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
 import org.jclouds.glesys.domain.DomainRecord;
 import org.jclouds.predicates.RetryablePredicate;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import static org.testng.Assert.assertTrue;
+import com.google.common.base.Predicate;
 
 /**
  * Tests behavior of {@code DomainClient}
  *
  * @author Adam Lowe
  */
-@Test(groups = "live", testName = "DomainClientLiveTest")
+@Test(groups = "live", testName = "DomainClientLiveTest", singleThreaded = true)
 public class DomainClientLiveTest extends BaseGleSYSClientLiveTest {
+   public final String testDomain = "glesystest.jclouds.org";
 
    @BeforeGroups(groups = {"live"})
    public void setupClient() {
@@ -55,6 +57,13 @@
                   return client.listRecords(testDomain).size() == value;
                }
             }, 30, 1, TimeUnit.SECONDS);
+
+      try {
+         client.deleteDomain(testDomain);
+      } catch (Exception ex) {
+      }
+      
+      createDomain(testDomain);
    }
 
    @AfterGroups(groups = {"live"})
@@ -67,24 +76,10 @@
    }
 
    private DomainClient client;
-   private String testDomain = "glesystest.jclouds.org";
-   private String testRecordId;
    private RetryablePredicate<Integer> domainCounter;
    private RetryablePredicate<Integer> recordCounter;
 
    @Test
-   public void testCreateDomain() throws Exception {
-      try {
-         client.deleteDomain(testDomain);
-      } catch (Exception ex) {
-      }
-
-      int before = client.listDomains().size();
-      client.addDomain(testDomain);
-      assertTrue(domainCounter.apply(before + 1));
-   }
-
-   @Test(dependsOnMethods = "testCreateDomain")
    public void testCreateRecord() throws Exception {
       int before = client.listRecords(testDomain).size();
 
@@ -93,7 +88,7 @@
       assertTrue(recordCounter.apply(before + 1));
    }
 
-   @Test(dependsOnMethods = "testCreateRecord")
+   @Test
    public void testDeleteRecord() throws Exception {
       Set<DomainRecord> domainRecords = client.listRecords(testDomain);
 
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/EmailAsyncClientTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/EmailAsyncClientTest.java
new file mode 100644
index 0000000..84fb0ca
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/EmailAsyncClientTest.java
@@ -0,0 +1,68 @@
+/**
+ * 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.glesys.features;
+
+import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.internal.RestAnnotationProcessor;
+import org.testng.annotations.Test;
+
+import com.google.inject.TypeLiteral;
+
+/**
+ * Tests annotation parsing of {@code ArchiveAsyncClient}
+ * 
+ * @author Adam Lowe
+ */
+@Test(groups = "unit", testName = "EmailAsyncClientTest")
+public class EmailAsyncClientTest extends BaseGleSYSAsyncClientTest<EmailAsyncClient> {
+   public EmailAsyncClientTest() {
+      asyncClientClass = EmailAsyncClient.class;
+      remoteServicePrefix = "email";
+   }
+
+   public void testList() throws Exception {
+      testMethod("listAccounts", "list", "POST", true, ReturnEmptySetOnNotFoundOr404.class, newEntry("domain","test"));
+   }
+
+   public void testOverview() throws Exception {
+      testMethod("getEmailOverview", "overview", "POST", true, ReturnEmptySetOnNotFoundOr404.class);
+   }
+
+   public void testCreateAccount() throws Exception {
+      testMethod("createAccount", "createaccount", "POST", false, MapHttp4xxCodesToExceptions.class, 
+            newEntry("emailaccount", "jclouds.org"), newEntry("password", "test@jclouds.org"));
+   }
+   
+   public void testCreateAlias() throws Exception {
+      testMethod("createAlias", "createalias", "POST", false, MapHttp4xxCodesToExceptions.class,
+            newEntry("emailalias", "test2@jclouds.org"), newEntry("goto", "test@jclouds.org"));
+   }
+   
+   public void testEditAlias() throws Exception {
+      testMethod("editAlias", "editalias", "POST", false, MapHttp4xxCodesToExceptions.class,
+            newEntry("emailalias", "test2@jclouds.org"), newEntry("goto", "test1@jclouds.org"));
+   }
+   
+   @Override
+   protected TypeLiteral<RestAnnotationProcessor<EmailAsyncClient>> createTypeLiteral() {
+      return new TypeLiteral<RestAnnotationProcessor<EmailAsyncClient>>() {
+      };
+   }
+}
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/EmailClientLiveTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/EmailClientLiveTest.java
new file mode 100644
index 0000000..845d173
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/EmailClientLiveTest.java
@@ -0,0 +1,157 @@
+/**
+ * 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.
+ */
+/**
+* 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.glesys.features;
+
+import com.google.common.base.Predicate;
+import org.jclouds.glesys.domain.*;
+import org.jclouds.glesys.options.EmailCreateOptions;
+import org.jclouds.glesys.options.EmailEditOptions;
+import org.jclouds.glesys.options.ServerDestroyOptions;
+import org.jclouds.predicates.RetryablePredicate;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static org.testng.Assert.*;
+
+/**
+* Tests behavior of {@code EmailClient}
+*
+* @author Adam Lowe
+*/
+@Test(groups = "live", testName = "EmailClientLiveTest", singleThreaded = true)
+public class EmailClientLiveTest extends BaseGleSYSClientLiveTest {
+
+   @BeforeGroups(groups = {"live"})
+   public void setupClient() {
+      super.setupClient();
+      client = context.getApi().getEmailClient();
+
+      try {
+         client.delete("test@" + testDomain);
+         client.delete("test2@" + testDomain);
+         context.getApi().getDomainClient().deleteDomain(testDomain);
+      } catch(Exception e) {
+      }
+
+      serverId = createServer("test-email-jclouds").getServerId();
+      
+      createDomain(testDomain);
+
+      emailAccountCounter = new RetryablePredicate<Integer>(
+            new Predicate<Integer>() {
+               public boolean apply(Integer value) {
+                  return client.listAccounts(testDomain).size() == value;
+               }
+            }, 30, 1, TimeUnit.SECONDS);
+      
+   }
+   
+
+   @AfterGroups(groups = {"live"})
+   public void tearDown() {
+      client.delete("test@" + testDomain);
+      assertTrue(emailAccountCounter.apply(0));
+      context.getApi().getDomainClient().deleteDomain(testDomain);
+      context.getApi().getServerClient().destroyServer(serverId, ServerDestroyOptions.Builder.discardIp());
+      super.tearDown();
+   }
+   
+   private EmailClient client;
+   private String serverId;
+   private final String testDomain = "email-test.jclouds.org";
+   private RetryablePredicate<Integer> emailAccountCounter;
+   
+   @Test
+   public void createEmail() {
+      client.createAccount("test@" + testDomain, "password", EmailCreateOptions.Builder.antiVirus(true));
+      assertTrue(emailAccountCounter.apply(1));
+   }
+
+   @Test(dependsOnMethods = "createEmail")
+   public void createAlias() {
+      client.createAlias("test2@" + testDomain, "test@" + testDomain);
+      EmailOverview overview = client.getEmailOverview();
+      assertTrue(overview.getSummary().getAliases() == 1);
+      client.delete("test2@" + testDomain);
+      overview = client.getEmailOverview();
+      assertTrue(overview.getSummary().getAliases() == 0);
+   }
+   
+   @Test(dependsOnMethods = "createEmail")
+   public void testOverview() throws Exception {
+      EmailOverview overview = client.getEmailOverview();
+      assertNotNull(overview.getSummary());
+      assertTrue(overview.getSummary().getAccounts() >= 1);
+      assertTrue(overview.getSummary().getAliases() == 0);
+      assertTrue(overview.getSummary().getMaxAccounts() > 0);
+      assertTrue(overview.getSummary().getMaxAliases() > 0);
+      assertNotNull(overview.getDomains());
+      assertFalse(overview.getDomains().isEmpty());
+      
+      EmailOverviewDomain domain = EmailOverviewDomain.builder().domain(testDomain).accounts(1).build();
+      assertTrue(overview.getDomains().contains(domain));
+   }
+
+   @Test(dependsOnMethods = "createEmail")
+   public void testListAccounts() throws Exception {
+      Set<Email> accounts = client.listAccounts(testDomain);
+      assertTrue(accounts.size() >= 1);
+   }
+
+   @Test(dependsOnMethods = "createEmail")
+   public void testEditAccount() throws Exception {
+      Set<Email> accounts = client.listAccounts(testDomain);
+      for(Email account : accounts) {
+         if (account.getAccount().equals("test@" + testDomain)) {
+            assertTrue(account.getAntiVirus());
+         }
+      }
+      
+      client.editAccount("test@" + testDomain, EmailEditOptions.Builder.antiVirus(false));
+      
+      accounts = client.listAccounts(testDomain);
+      for(Email account : accounts) {
+         if (account.getAccount().equals("test@" + testDomain)) {
+            assertFalse(account.getAntiVirus());
+         }
+      }
+   }
+}
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/IpAsyncClientTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/IpAsyncClientTest.java
index b1bc619..6fcea5b 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/IpAsyncClientTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/IpAsyncClientTest.java
@@ -18,6 +18,12 @@
  */
 package org.jclouds.glesys.features;
 
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.functions.ParseFirstJsonValueNamed;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
 import org.testng.annotations.Test;
 
@@ -31,6 +37,22 @@
 @Test(groups = "unit", testName = "IpAsyncClientTest")
 public class IpAsyncClientTest extends BaseGleSYSAsyncClientTest<IpAsyncClient> {
 
+   public void testGetIpDetails() throws SecurityException, NoSuchMethodException, IOException {
+      Method method = IpAsyncClient.class.getMethod("getIpDetails", String.class);
+      HttpRequest request = processor.createRequest(method, "31.192.227.37");
+
+      assertRequestLineEquals(request,
+               "GET https://api.glesys.com/ip/details/ipaddress/31.192.227.37/format/json HTTP/1.1");
+      assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
+      assertPayloadEquals(request, null, "application/xml", false);
+
+      assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
+
+      checkFilters(request);
+   }
+
    @Override
    protected TypeLiteral<RestAnnotationProcessor<IpAsyncClient>> createTypeLiteral() {
       return new TypeLiteral<RestAnnotationProcessor<IpAsyncClient>>() {
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/IpClientExpectTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/IpClientExpectTest.java
new file mode 100644
index 0000000..1f82c6c
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/IpClientExpectTest.java
@@ -0,0 +1,250 @@
+/**
+ * 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.glesys.features;
+
+import com.google.common.collect.ImmutableMultimap;
+import org.jclouds.glesys.GleSYSClient;
+import org.jclouds.glesys.domain.IpDetails;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.rest.BaseRestClientExpectTest;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptySet;
+import static org.jclouds.io.Payloads.newUrlEncodedFormPayload;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+import static org.testng.collections.Sets.newHashSet;
+
+/**
+ * Allows us to test a client via its side effects.
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "IpClientExpectTest")
+public class IpClientExpectTest extends BaseRestClientExpectTest<GleSYSClient> {
+   public IpClientExpectTest() {
+      provider = "glesys";
+   }
+
+   public void testGetIpDetailsWhenResponseIs2xx() {
+
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("GET").endpoint(
+                      URI.create("https://api.glesys.com/ip/details/ipaddress/31.192.227.37/format/json")).headers(
+                      ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
+                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+              HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_get_details.json")).build())
+              .getIpClient();
+
+      assertEquals(client.getIpDetails("31.192.227.37"), IpDetails.builder().datacenter("Falkenberg").ipversion("4")
+              .platform("OpenVZ").ptr("31-192-227-37-static.serverhotell.net.").build());
+
+   }
+
+   public void testGetIpDetailsWhenResponseIs4xxReturnsNull() {
+
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("GET").endpoint(
+                      URI.create("https://api.glesys.com/ip/details/ipaddress/31.192.227.37/format/json")).headers(
+                      ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
+                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+              HttpResponse.builder().statusCode(404).build()).getIpClient();
+
+      assertEquals(client.getIpDetails("31.192.227.37"), null);
+
+   }
+
+   public void testTakeWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("POST").endpoint(
+                      URI.create("https://api.glesys.com/ip/take/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder()
+                              .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                      .payload(newUrlEncodedFormPayload(
+                              ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
+                      )).build(),
+              HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_take.json")).build())
+              .getIpClient();
+
+      client.take("46.21.105.186");
+   }
+
+   public void testTakeWhenResponseIs4xxThrowsResponseException() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("POST").endpoint(
+                      URI.create("https://api.glesys.com/ip/take/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder()
+                              .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                      .payload(newUrlEncodedFormPayload(
+                              ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
+                      )).build(),
+              HttpResponse.builder().statusCode(400).build())
+              .getIpClient();
+
+      try {
+         client.take("46.21.105.186");
+         fail();
+      } catch (HttpResponseException e) {
+         // Expected
+      }
+   }
+
+   public void testReleaseWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("POST").endpoint(
+                      URI.create("https://api.glesys.com/ip/release/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder()
+                              .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                      .payload(newUrlEncodedFormPayload(
+                              ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
+                      )).build(),
+              HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_release.json")).build())
+              .getIpClient();
+
+      client.release("46.21.105.186");
+   }
+
+   public void testReleaseWhenResponseIs4xxThrowsResponseException() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("POST").endpoint(
+                      URI.create("https://api.glesys.com/ip/release/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder()
+                              .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                      .payload(newUrlEncodedFormPayload(
+                              ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
+                      )).build(),
+              HttpResponse.builder().statusCode(400).build())
+              .getIpClient();
+
+      try {
+         client.release("46.21.105.186");
+         fail();
+      } catch (HttpResponseException e) {
+         // Expected
+      }
+   }
+
+   public void testListFreeWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("GET").endpoint(
+                      URI.create("https://api.glesys.com/ip/listfree/ipversion/4/datacenter/Falkenberg/platform/OpenVZ/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
+                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+              HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_list_free.json")).build())
+              .getIpClient();
+
+      Set<Object> expectedIps = newHashSet();
+      expectedIps.addAll(asList("31.192.226.131", "31.192.226.133"));
+      assertEquals(client.listFree("4", "Falkenberg", "OpenVZ"), expectedIps);
+   }
+
+   public void testListFreeWhenResponseIs404ReturnsEmptySet() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("GET").endpoint(
+                      URI.create("https://api.glesys.com/ip/listfree/ipversion/4/datacenter/Falkenberg/platform/OpenVZ/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
+                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+              HttpResponse.builder().statusCode(404).build())
+              .getIpClient();
+
+      assertEquals(client.listFree("4", "Falkenberg", "OpenVZ"), emptySet());
+   }
+
+   public void testAddWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("POST").endpoint(
+                      URI.create("https://api.glesys.com/ip/add/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder().put(
+                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                      .payload(newUrlEncodedFormPayload(
+                              ImmutableMultimap.<String, String>builder()
+                                      .put("ipaddress", "31.192.227.37")
+                                      .put("serverid", "vz1946889").build())).build(),
+              HttpResponse.builder().statusCode(200).build())
+              .getIpClient();
+
+      client.addIpToServer("31.192.227.37", "vz1946889");
+   }
+
+   public void testAddWhenResponseIs4xxThrowsHttpException() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("POST").endpoint(
+                      URI.create("https://api.glesys.com/ip/add/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder().put(
+                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                      .payload(newUrlEncodedFormPayload(
+                              ImmutableMultimap.<String, String>builder()
+                                      .put("ipaddress", "31.192.227.37")
+                                      .put("serverid", "vz1946889")
+                                      .build())).build(),
+              HttpResponse.builder().statusCode(400).build())
+              .getIpClient();
+
+      try {
+         client.addIpToServer("31.192.227.37", "vz1946889");
+         fail();
+      } catch (HttpResponseException e) {
+         // Expected
+      }
+   }
+
+   public void testRemoveWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("POST").endpoint(
+                      URI.create("https://api.glesys.com/ip/remove/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder().put(
+                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                      .payload(newUrlEncodedFormPayload(
+                              ImmutableMultimap.<String, String>builder()
+                                      .put("ipaddress", "31.192.227.37")
+                                      .put("serverid", "vz1946889").build())).build(),
+              HttpResponse.builder().statusCode(200).build())
+              .getIpClient();
+
+      client.removeIpFromServer("31.192.227.37", "vz1946889");
+   }
+
+   public void testRemoveWhenResponseIs4xxThrowsHttpException() {
+      IpClient client = requestSendsResponse(
+              HttpRequest.builder().method("POST").endpoint(
+                      URI.create("https://api.glesys.com/ip/remove/format/json"))
+                      .headers(ImmutableMultimap.<String, String>builder().put(
+                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                      .payload(newUrlEncodedFormPayload(
+                              ImmutableMultimap.<String, String>builder()
+                                      .put("ipaddress", "31.192.227.37")
+                                      .put("serverid", "vz1946889").build())).build(),
+              HttpResponse.builder().statusCode(400).build())
+              .getIpClient();
+
+      try {
+         client.removeIpFromServer("31.192.227.37", "vz1946889");
+         fail();
+      } catch (HttpResponseException e) {
+         // Expected
+      }
+   }
+
+}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ServerAsyncClientTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ServerAsyncClientTest.java
index 8a659e6..283c622 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ServerAsyncClientTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ServerAsyncClientTest.java
@@ -18,20 +18,22 @@
  */
 package org.jclouds.glesys.features;
 
-import com.google.common.collect.Maps;
-import com.google.inject.TypeLiteral;
-import org.jclouds.glesys.options.*;
-import org.jclouds.http.HttpRequest;
-import org.jclouds.http.functions.ParseFirstJsonValueNamed;
+import java.util.Map;
+
+import org.jclouds.glesys.functions.ParseServerTemplatesFromHttpResponse;
+import org.jclouds.glesys.options.ServerCloneOptions;
+import org.jclouds.glesys.options.ServerCreateOptions;
+import org.jclouds.glesys.options.ServerDestroyOptions;
+import org.jclouds.glesys.options.ServerEditOptions;
+import org.jclouds.glesys.options.ServerStatusOptions;
+import org.jclouds.glesys.options.ServerStopOptions;
 import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
 import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
 import org.testng.annotations.Test;
 
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.Map;
+import com.google.inject.TypeLiteral;
 
 /**
  * Tests annotation parsing of {@code ServerAsyncClient}
@@ -58,7 +60,8 @@
    }
 
    public void testGetTemplates() throws Exception {
-      testMethod("getTemplates", "templates", "GET", true, MapHttp4xxCodesToExceptions.class);
+      testMethod("getTemplates", "templates", "GET", true, ParseServerTemplatesFromHttpResponse.class,
+               MapHttp4xxCodesToExceptions.class);
    }
 
    public void testGetServer() throws Exception {
@@ -120,6 +123,11 @@
       testMethod("rebootServer", "reboot", "POST", false, MapHttp4xxCodesToExceptions.class, serverIdOnly);
    }
 
+   public void testDestroyServer() throws Exception {
+      testMethod("destroyServer", "destroy", "POST", false, MapHttp4xxCodesToExceptions.class, serverIdOnly, ServerDestroyOptions.Builder.keepIp());
+      testMethod("destroyServer", "destroy", "POST", false, MapHttp4xxCodesToExceptions.class, serverIdOnly, ServerDestroyOptions.Builder.discardIp());
+   }
+
    @Override
    protected TypeLiteral<RestAnnotationProcessor<ServerAsyncClient>> createTypeLiteral() {
       return new TypeLiteral<RestAnnotationProcessor<ServerAsyncClient>>() {
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ServerClientLiveTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ServerClientLiveTest.java
index 38e61c3..ee0230e 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ServerClientLiveTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/features/ServerClientLiveTest.java
@@ -21,10 +21,12 @@
 import com.google.common.base.Predicate;
 import org.jclouds.glesys.domain.*;
 import org.jclouds.glesys.options.ServerCloneOptions;
+import org.jclouds.glesys.options.ServerDestroyOptions;
 import org.jclouds.glesys.options.ServerStatusOptions;
 import org.jclouds.predicates.RetryablePredicate;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import java.util.Map;
@@ -41,39 +43,34 @@
  */
 @Test(groups = "live", testName = "ServerClientLiveTest")
 public class ServerClientLiveTest extends BaseGleSYSClientLiveTest {
-
+   public static final String testHostName1 = "jclouds-test";
+   public static final String testHostName2 = "jclouds-test2";
+   
    @BeforeGroups(groups = {"live"})
    public void setupClient() {
       super.setupClient();
       client = context.getApi().getServerClient();
+      serverStatusChecker = createServer(testHostName1);
+      testServerId = serverStatusChecker.getServerId();
    }
 
    @AfterGroups(groups = {"live"})
    public void tearDown() {
-      client.destroyServer(testServer.getId(), 0);
-      if (testServer2 != null) {
-         client.destroyServer(testServer2.getId(), 0);
+      client.destroyServer(testServerId, ServerDestroyOptions.Builder.discardIp());
+      if (testServerId2 != null) {
+         client.destroyServer(testServerId2, ServerDestroyOptions.Builder.discardIp());
       }
       super.tearDown();
    }
 
    private ServerClient client;
-   private ServerCreated testServer;
-   private ServerCreated testServer2;
-   // note this is initialized by testCreateServer()
-   private RetryablePredicate<ServerState> runningServerCounter;
+   private ServerStatusChecker serverStatusChecker;
+   private String testServerId;
+   private String testServerId2;
 
-   @Test
-   public void testCreateServer() throws Exception {
-      testServer = client.createServer("Falkenberg", "OpenVZ", "jclouds-test", "Ubuntu 10.04 LTS 32-bit", 5, 512, 1, "password", 50);
-      
-      assertNotNull(testServer.getId());
-      assertEquals(testServer.getHostname(), "jclouds-test");
-      assertFalse(testServer.getIps().isEmpty());
-      
-      runningServerCounter = new ServerStatusChecker(testServer.getId(), 120, 2, TimeUnit.SECONDS);
-
-      assertTrue(runningServerCounter.apply(ServerState.RUNNING));
+   @BeforeMethod
+   public void makeSureServerIsRunning() throws Exception {
+      serverStatusChecker.apply(ServerState.RUNNING);
    }
    
    @Test
@@ -125,7 +122,7 @@
       assert t.getMinMemSize() > 0 : t;
     }
    
-   @Test(dependsOnMethods = "testCreateServer")
+   @Test
    public void testListServers() throws Exception {
       Set<Server> response = client.listServers();
       assertNotNull(response);
@@ -141,35 +138,48 @@
       }
    }
 
-   @Test(dependsOnMethods = "testCreateServer")
+   @Test
    public void testServerDetails() throws Exception {
-      ServerStatus newStatus = client.getServerStatus(testServer.getId());
+      ServerStatus newStatus = client.getServerStatus(testServerId);
       checkStatus(newStatus);
    }
 
-   @Test(dependsOnMethods = "testCreateServer")
+   @Test(enabled=false) // TODO work a better plan
    public void testRebootServer() throws Exception {
-      client.rebootServer(testServer.getId());
+      long uptime = 0;
+      
+      while(uptime < 20) {
+         uptime = client.getServerStatus(testServerId).getUptime();
+      }
+      
+      assertTrue(uptime > 19);
+      
+      client.rebootServer(testServerId);
+      
+      Thread.sleep(1000);
 
-      assertTrue(runningServerCounter.apply(ServerState.STOPPED));
-      assertTrue(runningServerCounter.apply(ServerState.RUNNING));
+      uptime = client.getServerStatus(testServerId).getUptime();
+      
+      assertTrue(uptime < 20);
+
+      assertTrue(serverStatusChecker.apply(ServerState.RUNNING));
    }
 
-   @Test(dependsOnMethods = "testCreateServer")
+   @Test(enabled=false) // TODO
    public void testStopAndStartServer() throws Exception {
-      client.stopServer(testServer.getId());
+      client.stopServer(testServerId);
 
-      assertTrue(runningServerCounter.apply(ServerState.STOPPED));
+      assertTrue(serverStatusChecker.apply(ServerState.STOPPED));
 
-      client.startServer(testServer.getId());
+      client.startServer(testServerId);
 
-      assertTrue(runningServerCounter.apply(ServerState.RUNNING));
+      assertTrue(serverStatusChecker.apply(ServerState.RUNNING));
    }
 
 
-   @Test(dependsOnMethods = "testCreateServer")
+   @Test
    public void testServerLimits() throws Exception {
-      Map<String, ServerLimit> limits = client.getServerLimits(testServer.getId());
+      Map<String, ServerLimit> limits = client.getServerLimits(testServerId);
       assertNotNull(limits);
       for (Map.Entry<String, ServerLimit> entry : limits.entrySet()) {
          assertNotNull(entry.getKey());
@@ -183,27 +193,27 @@
       }
    }
 
-   // TODO in progress
-   @Test(enabled=false, dependsOnMethods = "testCreateServer")
+   @Test
    public void testServerConsole() throws Exception {
-      ServerConsole console = client.getServerConsole(testServer.getId());
+      ServerConsole console = client.getServerConsole(testServerId);
       assertNotNull(console);
       assertNotNull(console.getHost());
       assertTrue(console.getPort() > 0 && console.getPort() < 65537);
       assertNotNull(console.getPassword());
    }
 
-   // takes a few minutes
-   @Test(enabled=false, dependsOnMethods = "testCreateServer")
+   // takes a few minutes and requires an extra server (using 2 already)
+   @Test(enabled=false)
    public void testCloneServer() throws Exception {
-      testServer2 = client.cloneServer(testServer.getId(), "jclouds-test2", ServerCloneOptions.Builder.cpucores(1));
+      ServerCreated testServer2 = client.cloneServer(testServerId, testHostName2, ServerCloneOptions.Builder.cpucores(1));
 
       assertNotNull(testServer2.getId());
       assertEquals(testServer2.getHostname(), "jclouds-test2");
       assertTrue(testServer2.getIps().isEmpty());
+      
+      testServerId2 = testServer2.getId();
 
-      RetryablePredicate<ServerState> cloneChecker = new ServerStatusChecker(testServer2.getId(), 300, 10, TimeUnit.SECONDS);
-
+      RetryablePredicate<ServerState> cloneChecker = new ServerStatusChecker(client, testServerId2, 300, 10, TimeUnit.SECONDS);
       assertTrue(cloneChecker.apply(ServerState.STOPPED));
 
       client.startServer(testServer2.getId());
@@ -213,12 +223,12 @@
             new Predicate<ServerState>() {
 
                public boolean apply(ServerState value) {
-                  ServerStatus status = client.getServerStatus(testServer2.getId(), ServerStatusOptions.Builder.state());
+                  ServerStatus status = client.getServerStatus(testServerId2, ServerStatusOptions.Builder.state());
                   if (status.getState() == value) {
                      return true;
                   }
-                  
-                  client.startServer(testServer2.getId());
+
+                  client.startServer(testServerId2);
                   return false;
                }
 
@@ -265,17 +275,4 @@
       assert status.getMemory().getUsage() >= 0 : status;
       assertNotNull(status.getMemory().getUnit());
    }
-
-   private class ServerStatusChecker extends RetryablePredicate<ServerState> {
-      public ServerStatusChecker(final String serverId, long maxWait, long period, TimeUnit unit) {
-         super(new Predicate<ServerState>() {
-
-            public boolean apply(ServerState value) {
-               ServerStatus status = client.getServerStatus(serverId, ServerStatusOptions.Builder.state());
-               return status.getState() == value;
-            }
-
-         }, maxWait, period, unit);
-      }
-   }
 }
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveAllowedArgumentsTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveAllowedArgumentsTest.java
index d42147b..27090a6 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveAllowedArgumentsTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveAllowedArgumentsTest.java
@@ -19,20 +19,18 @@
 package org.jclouds.glesys.parse;
 
 
-import com.google.inject.Guice;
-import com.google.inject.Injector;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.glesys.config.GleSYSParserModule;
 import org.jclouds.glesys.domain.ArchiveAllowedArguments;
-import org.jclouds.glesys.domain.ServerAllowedArguments;
 import org.jclouds.json.BaseItemParserTest;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.core.MediaType;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
 
 /**
  * @author Adam Lowe
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveDetailsTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveDetailsTest.java
index 36f2c1f..250fe8f 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveDetailsTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveDetailsTest.java
@@ -18,22 +18,18 @@
  */
 package org.jclouds.glesys.parse;
 
-import com.google.common.collect.ImmutableSet;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.glesys.config.GleSYSParserModule;
-import org.jclouds.glesys.domain.Archive;
 import org.jclouds.glesys.domain.ArchiveDetails;
 import org.jclouds.json.BaseItemParserTest;
-import org.jclouds.json.BaseParserTest;
-import org.jclouds.json.BaseSetParserTest;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.core.MediaType;
-import java.util.Set;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
 
 /**
  * 
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveListTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveListTest.java
index a79e74f..28df00e 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveListTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseArchiveListTest.java
@@ -18,20 +18,21 @@
  */
 package org.jclouds.glesys.parse;
 
-import com.google.common.collect.ImmutableSet;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.glesys.config.GleSYSParserModule;
 import org.jclouds.glesys.domain.Archive;
-import org.jclouds.glesys.domain.Server;
 import org.jclouds.json.BaseSetParserTest;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.core.MediaType;
-import java.util.Set;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
 
 /**
  * 
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseDomainListTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseDomainListTest.java
index 9b49caf..1ff0727 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseDomainListTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseDomainListTest.java
@@ -19,9 +19,16 @@
 package org.jclouds.glesys.parse;
 
 
-import com.google.common.collect.Sets;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
+import static org.testng.Assert.fail;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.glesys.config.GleSYSParserModule;
 import org.jclouds.glesys.domain.Domain;
 import org.jclouds.json.BaseSetParserTest;
@@ -29,15 +36,9 @@
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.core.MediaType;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Set;
-
-import static org.testng.Assert.fail;
+import com.google.common.collect.Sets;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
 
 /**
  * @author Adam Lowe
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseDomainRecordListTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseDomainRecordListTest.java
index 4ecd842..f65de7b 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseDomainRecordListTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseDomainRecordListTest.java
@@ -19,27 +19,22 @@
 package org.jclouds.glesys.parse;
 
 
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Sets;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
+import java.util.Arrays;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.glesys.config.GleSYSParserModule;
-import org.jclouds.glesys.domain.Domain;
 import org.jclouds.glesys.domain.DomainRecord;
 import org.jclouds.json.BaseSetParserTest;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.core.MediaType;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Set;
-
-import static org.testng.Assert.fail;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
 
 /**
  * @author Adam Lowe
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseEmailListTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseEmailListTest.java
new file mode 100644
index 0000000..4503371
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseEmailListTest.java
@@ -0,0 +1,69 @@
+/**
+ * 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.glesys.parse;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.jclouds.glesys.config.GleSYSParserModule;
+import org.jclouds.glesys.domain.Email;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Set;
+
+/**
+ * @author Adam Lowe
+ */
+@Test(groups = "unit", testName = "ParseEmailListTest")
+public class ParseEmailListTest extends BaseSetParserTest<Email> {
+
+   @Override
+   public String resource() {
+      return "/email_list.json";
+   }
+
+   @Override
+   @SelectJson("emailaccounts")
+   @Consumes(MediaType.APPLICATION_JSON)
+   public Set<Email> expected() {
+      DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+      Email.Builder builder = Email.builder().quota("200 MB").usedQuota("0 MB").antispamLevel(3).antiVirus(true).autoRespond(false).autoRespondSaveEmail(true).autoRespondMessage("false");
+      try {
+         return ImmutableSet.of(
+               builder.account("test@adamlowe.net").created(dateFormat.parse("2011-12-22T12:13:14")).modified(dateFormat.parse("2011-12-22T12:13:35")).build(),
+               builder.account("test2@adamlowe.net").created(dateFormat.parse("2011-12-22T12:14:29")).modified(dateFormat.parse("2011-12-22T12:14:31")).build()
+         );
+      } catch(ParseException ex) {
+         throw new RuntimeException(ex);
+      }
+   }
+
+   protected Injector injector() {
+      return Guice.createInjector(new GleSYSParserModule(), new GsonModule());
+   }
+
+}
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseEmailOverviewTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseEmailOverviewTest.java
new file mode 100644
index 0000000..afc53f1
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseEmailOverviewTest.java
@@ -0,0 +1,57 @@
+/**
+ * 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.glesys.parse;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.jclouds.glesys.config.GleSYSParserModule;
+import org.jclouds.glesys.domain.EmailOverview;
+import org.jclouds.glesys.domain.EmailOverviewDomain;
+import org.jclouds.glesys.domain.EmailOverviewSummary;
+import org.jclouds.json.BaseItemParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Adam Lowe
+ */
+@Test(groups = "unit", testName = "ParseEmailListTest")
+public class ParseEmailOverviewTest extends BaseItemParserTest<EmailOverview> {
+
+   @Override
+   public String resource() {
+      return "/email_overview.json";
+   }
+
+   @Override
+   @SelectJson("response")
+   @Consumes(MediaType.APPLICATION_JSON)
+   public EmailOverview expected() {
+      return EmailOverview.builder().summary(EmailOverviewSummary.builder().accounts(2).aliases(0).maxAccounts(50).maxAliases(1000).build()).domains(EmailOverviewDomain.builder().accounts(2).aliases(0).domain("adamlowe.net").build()).build();
+   }
+
+   protected Injector injector() {
+      return Guice.createInjector(new GleSYSParserModule(), new GsonModule());
+   }
+
+}
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseIpAddressFromResponseTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseIpAddressFromResponseTest.java
index 9653a64..8284e9c 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseIpAddressFromResponseTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseIpAddressFromResponseTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.glesys.parse;
 
 import com.google.inject.Guice;
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerConsoleTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerConsoleTest.java
new file mode 100644
index 0000000..5e8905b
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerConsoleTest.java
@@ -0,0 +1,56 @@
+/**
+ * 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.glesys.parse;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.glesys.config.GleSYSParserModule;
+import org.jclouds.glesys.domain.ServerConsole;
+import org.jclouds.json.BaseItemParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * @author Adam Lowe
+ */
+@Test(groups = "unit", testName = "ParseServerCreatedTest")
+public class ParseServerConsoleTest extends BaseItemParserTest<ServerConsole> {
+
+   @Override
+   public String resource() {
+      return "/server_console.json";
+   }
+
+   @Override
+   @SelectJson("remote")
+   @Consumes(MediaType.APPLICATION_JSON)
+   public ServerConsole expected() {
+      return ServerConsole.builder().host("79.99.2.147").port(59478).password("1476897311").build();
+   }
+
+   protected Injector injector() {
+      return Guice.createInjector(new GleSYSParserModule(), new GsonModule());
+   }
+}
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerCreatedTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerCreatedTest.java
index cd1b648..f08739f 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerCreatedTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerCreatedTest.java
@@ -47,7 +47,7 @@
    @SelectJson("server")
    @Consumes(MediaType.APPLICATION_JSON)
    public ServerCreated expected() {
-      return ServerCreated.builder().id("xm3630641").hostname("jclouds-test-host").ips(ServerCreatedIp.builder().ip("109.74.10.27").version(4).cost(2.00).build()).build();
+      return ServerCreated.builder().id("xm3630641").hostname("jclouds-test-host").ips(ServerCreatedIp.builder().ip("109.74.10.27").version4().cost(2.00).build()).build();
    }
     
 
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerStatusTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerStatusTest.java
index e398100..4d1e0f5 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerStatusTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerStatusTest.java
@@ -19,15 +19,14 @@
 package org.jclouds.glesys.parse;
 
 
-import com.google.inject.Guice;
-import com.google.inject.Injector;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.glesys.config.GleSYSParserModule;
 import org.jclouds.glesys.domain.Bandwidth;
-import org.jclouds.glesys.domain.Cost;
 import org.jclouds.glesys.domain.Cpu;
 import org.jclouds.glesys.domain.Disk;
 import org.jclouds.glesys.domain.Memory;
-import org.jclouds.glesys.domain.ServerDetails;
 import org.jclouds.glesys.domain.ServerState;
 import org.jclouds.glesys.domain.ServerStatus;
 import org.jclouds.json.BaseItemParserTest;
@@ -35,8 +34,8 @@
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.core.MediaType;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
 
 /**
  * @author Adam Lowe
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerTemplatesTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerTemplatesTest.java
index e42f01b..64d0730 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerTemplatesTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseServerTemplatesTest.java
@@ -18,26 +18,29 @@
  */
 package org.jclouds.glesys.parse;
 
+import java.util.Set;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.core.MediaType;
 
-import java.util.*;
+import org.jclouds.glesys.config.GleSYSParserModule;
+import org.jclouds.glesys.domain.ServerTemplate;
+import org.jclouds.glesys.functions.ParseServerTemplatesFromHttpResponse;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import org.jclouds.glesys.config.GleSYSParserModule;
-import org.jclouds.glesys.domain.*;
-import org.jclouds.json.BaseItemParserTest;
-import org.jclouds.json.config.GsonModule;
-import org.jclouds.rest.annotations.SelectJson;
-import org.testng.annotations.Test;
 
 /**
  * @author Adam Lowe
  */
 @Test(groups = "unit", testName = "ParseServerTemplatesTest")
-public class ParseServerTemplatesTest extends BaseItemParserTest<Map<String, Set<ServerTemplate>>> {
+public class ParseServerTemplatesTest extends BaseSetParserTest<ServerTemplate> {
 
    @Override
    public String resource() {
@@ -45,40 +48,29 @@
    }
 
    @Override
-   @SelectJson("templates")
+   @ResponseParser(ParseServerTemplatesFromHttpResponse.class)
    @Consumes(MediaType.APPLICATION_JSON)
-   public Map<String, Set<ServerTemplate>> expected() {
-      Map<String, Set<ServerTemplate>> result = new LinkedHashMap<String, Set<ServerTemplate>>();
-      
-      String[] vzNames = new String[]{
-            "Centos 5", "Centos 5 64-bit", "Centos 6 32-bit", "Centos 6 64-bit",
-            "Debian 5.0 32-bit", "Debian 5.0 64-bit", "Debian 6.0 32-bit", "Debian 6.0 64-bit", 
-            "Fedora Core 11", "Fedora Core 11 64-bit", "Gentoo", "Gentoo 64-bit",
-            "Scientific Linux 6", "Scientific Linux 6 64-bit", "Slackware 12",
-            "Ubuntu 10.04 LTS 32-bit", "Ubuntu 10.04 LTS 64-bit", "Ubuntu 11.04 64-bit"
-      };
-      String[] xenLinuxNames = new String[] {
-            "CentOS 5.5 x64", "CentOS 5.5 x86", "Centos 6 x64", "Centos 6 x86", "Debian-6 x64", "Debian 5.0.1 x64",
-            "FreeBSD 8.2", "Gentoo 10.1 x64", "Ubuntu 8.04 x64", "Ubuntu 10.04 LTS 64-bit", "Ubuntu 10.10 x64", "Ubuntu 11.04 x64",
-      };
-      String[] xenWindowsNames = new String[] {
-            "Windows Server 2008 R2 x64 std", "Windows Server 2008 R2 x64 web", "Windows Server 2008 x64 web", "Windows Server 2008 x86 web"
-      };
+   public Set<ServerTemplate> expected() {
+      Builder<ServerTemplate> builder = ImmutableSet.<ServerTemplate> builder();
 
-      result.put("OpenVZ", new HashSet<ServerTemplate>());
-      for (String name : vzNames) {
-         result.get("OpenVZ").add(new ServerTemplate(name, 5, 128, "linux", "OpenVZ"));
+      for (String name : new String[] { "Centos 5", "Centos 5 64-bit", "Centos 6 32-bit", "Centos 6 64-bit",
+               "Debian 5.0 32-bit", "Debian 5.0 64-bit", "Debian 6.0 32-bit", "Debian 6.0 64-bit", "Fedora Core 11",
+               "Fedora Core 11 64-bit", "Gentoo", "Gentoo 64-bit", "Scientific Linux 6", "Scientific Linux 6 64-bit",
+               "Slackware 12", "Ubuntu 10.04 LTS 32-bit", "Ubuntu 10.04 LTS 64-bit", "Ubuntu 11.04 64-bit" }) {
+         builder.add(new ServerTemplate(name, 5, 128, "linux", "OpenVZ"));
       }
 
-      result.put("Xen", new HashSet<ServerTemplate>());
-      for (String name : xenLinuxNames) {
-         result.get("Xen").add(new ServerTemplate(name, 5, 512, name.startsWith("FreeBSD") ? "freebsd" : "linux", "Xen"));
+      for (String name : new String[] { "CentOS 5.5 x64", "CentOS 5.5 x86", "Centos 6 x64", "Centos 6 x86",
+               "Debian-6 x64", "Debian 5.0.1 x64", "FreeBSD 8.2", "Gentoo 10.1 x64", "Ubuntu 8.04 x64",
+               "Ubuntu 10.04 LTS 64-bit", "Ubuntu 10.10 x64", "Ubuntu 11.04 x64" }) {
+         builder.add(new ServerTemplate(name, 5, 512, name.startsWith("FreeBSD") ? "freebsd" : "linux", "Xen"));
       }
-      for (String name : xenWindowsNames) {
-         result.get("Xen").add(new ServerTemplate(name, 20, 1024, "windows", "Xen"));
+      for (String name : new String[] { "Windows Server 2008 R2 x64 std", "Windows Server 2008 R2 x64 web",
+               "Windows Server 2008 x64 web", "Windows Server 2008 x86 web" }) {
+         builder.add(new ServerTemplate(name, 20, 1024, "windows", "Xen"));
       }
-    
-      return result;
+
+      return builder.build();
    }
 
    protected Injector injector() {
diff --git a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseSimpleIpDetailsTest.java b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseSimpleIpDetailsTest.java
index 52c0c98..d7dc291 100644
--- a/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseSimpleIpDetailsTest.java
+++ b/sandbox-providers/glesys/src/test/java/org/jclouds/glesys/parse/ParseSimpleIpDetailsTest.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.glesys.parse;
 
 import com.google.inject.Guice;
diff --git a/sandbox-providers/glesys/src/test/resources/email_list.json b/sandbox-providers/glesys/src/test/resources/email_list.json
new file mode 100644
index 0000000..b970ad8
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/resources/email_list.json
@@ -0,0 +1 @@
+{"response":{"status":{"code":"200","text":"OK"},"list":{"emailaccounts":[{"emailaccount":"test2@adamlowe.net","quota":"200 MB","usedquota":"0 MB","antispamlevel":3,"antivirus":true,"autorespond":false,"autorespondmessage":false,"autorespondsaveemail":true,"created":"2011-12-22 12:14:29","modified":"2011-12-22 12:14:31"},{"emailaccount":"test@adamlowe.net","quota":"200 MB","usedquota":"0 MB","antispamlevel":3,"antivirus":true,"autorespond":false,"autorespondmessage":false,"autorespondsaveemail":true,"created":"2011-12-22 12:13:14","modified":"2011-12-22 12:13:35"}]},"debug":{"input":{"domain":"adamlowe.net"}}}}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/test/resources/email_overview.json b/sandbox-providers/glesys/src/test/resources/email_overview.json
new file mode 100644
index 0000000..d98a8c8
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/resources/email_overview.json
@@ -0,0 +1 @@
+{"response":{"status":{"code":"200","text":"OK"},"summary":{"accounts":2,"maxaccounts":"50","aliases":0,"maxaliases":1000},"domains":[{"domain":"adamlowe.net","accounts":2,"aliases":0}],"debug":{"input":[]}}}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/test/resources/ip_release.json b/sandbox-providers/glesys/src/test/resources/ip_release.json
new file mode 100644
index 0000000..1367493
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/resources/ip_release.json
@@ -0,0 +1 @@
+{"response":{"status":{"code":"200","text":"OK"},"debug":{"input":{"ipaddress":"31.192.227.37"}}}}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/test/resources/ip_take.json b/sandbox-providers/glesys/src/test/resources/ip_take.json
new file mode 100644
index 0000000..e0d9e40
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/resources/ip_take.json
@@ -0,0 +1 @@
+{"response":{"status":{"code":"200","text":"OK"},"debug":{"input":{"ipaddress":"46.21.105.186"}}}}
\ No newline at end of file
diff --git a/sandbox-providers/glesys/src/test/resources/server_console.json b/sandbox-providers/glesys/src/test/resources/server_console.json
new file mode 100644
index 0000000..ae49ef9
--- /dev/null
+++ b/sandbox-providers/glesys/src/test/resources/server_console.json
@@ -0,0 +1 @@
+{"response":{"status":{"code":"200","text":"OK"},"remote":{"host":"79.99.2.147","port":"59478","password":"1476897311"},"debug":{"input":{"serverid":"vz1842554"}}}}
\ No newline at end of file
diff --git a/sandbox-providers/googlestorage/pom.xml b/sandbox-providers/googlestorage/pom.xml
index 57bc2e7..c42aab0 100644
--- a/sandbox-providers/googlestorage/pom.xml
+++ b/sandbox-providers/googlestorage/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.googlestorage.blobstore.GoogleStorageTestInitializer</test.initializer>
         <test.googlestorage.endpoint>https://commondatastorage.googleapis.com</test.googlestorage.endpoint>
-        <test.googlestorage.apiversion>2006-03-01</test.googlestorage.apiversion>
+        <test.googlestorage.api-version>2006-03-01</test.googlestorage.api-version>
+        <test.googlestorage.build-version></test.googlestorage.build-version>
         <test.googlestorage.identity>FIX_ME</test.googlestorage.identity>
         <test.googlestorage.credential>FIX_ME</test.googlestorage.credential>
     </properties>
@@ -101,7 +102,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.googlestorage.endpoint>${test.googlestorage.endpoint}</test.googlestorage.endpoint>
-                                        <test.googlestorage.apiversion>${test.googlestorage.apiversion}</test.googlestorage.apiversion>
+                                        <test.googlestorage.api-version>${test.googlestorage.api-version}</test.googlestorage.api-version>
+                                        <test.googlestorage.build-version>${test.googlestorage.build-version}</test.googlestorage.build-version>
                                         <test.googlestorage.identity>${test.googlestorage.identity}</test.googlestorage.identity>
                                         <test.googlestorage.credential>${test.googlestorage.credential}</test.googlestorage.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/sandbox-providers/hosteurope-storage/pom.xml b/sandbox-providers/hosteurope-storage/pom.xml
index 6c01b63..2b51626 100644
--- a/sandbox-providers/hosteurope-storage/pom.xml
+++ b/sandbox-providers/hosteurope-storage/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.hosteurope.storage.blobstore.HostEuropeStorageTestInitializer</test.initializer>
         <test.hosteurope-storage.endpoint>https://cs.hosteurope.de</test.hosteurope-storage.endpoint>
-        <test.hosteurope-storage.apiversion>2006-03-01</test.hosteurope-storage.apiversion>
+        <test.hosteurope-storage.api-version>2006-03-01</test.hosteurope-storage.api-version>
+        <test.hosteurope-storage.build-version></test.hosteurope-storage.build-version>
         <test.hosteurope-storage.identity>FIX_ME</test.hosteurope-storage.identity>
         <test.hosteurope-storage.credential>FIX_ME</test.hosteurope-storage.credential>
     </properties>
@@ -108,7 +109,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.hosteurope-storage.endpoint>${test.hosteurope-storage.endpoint}</test.hosteurope-storage.endpoint>
-                                        <test.hosteurope-storage.apiversion>${test.hosteurope-storage.apiversion}</test.hosteurope-storage.apiversion>
+                                        <test.hosteurope-storage.api-version>${test.hosteurope-storage.api-version}</test.hosteurope-storage.api-version>
+                                        <test.hosteurope-storage.build-version>${test.hosteurope-storage.build-version}</test.hosteurope-storage.build-version>
                                         <test.hosteurope-storage.identity>${test.hosteurope-storage.identity}</test.hosteurope-storage.identity>
                                         <test.hosteurope-storage.credential>${test.hosteurope-storage.credential}</test.hosteurope-storage.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/sandbox-providers/ibm-smartcloud/pom.xml b/sandbox-providers/ibm-smartcloud/pom.xml
index 08f70f0..c8ea4dd 100644
--- a/sandbox-providers/ibm-smartcloud/pom.xml
+++ b/sandbox-providers/ibm-smartcloud/pom.xml
@@ -49,7 +49,8 @@
 
     <properties>
         <test.ibm-smartcloud.endpoint>https://www-147.ibm.com/computecloud/enterprise/api/rest</test.ibm-smartcloud.endpoint>
-        <test.ibm-smartcloud.apiversion>20100331</test.ibm-smartcloud.apiversion>
+        <test.ibm-smartcloud.api-version>20100331</test.ibm-smartcloud.api-version>
+        <test.ibm-smartcloud.build-version></test.ibm-smartcloud.build-version>
         <test.ibm-smartcloud.identity>FIXME</test.ibm-smartcloud.identity>
         <test.ibm-smartcloud.credential>FIXME</test.ibm-smartcloud.credential>
         <test.ibm-smartcloud.image-id></test.ibm-smartcloud.image-id>
@@ -110,7 +111,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.ibm-smartcloud.endpoint>${test.ibm-smartcloud.endpoint}</test.ibm-smartcloud.endpoint>
-                                        <test.ibm-smartcloud.apiversion>${test.ibm-smartcloud.apiversion}</test.ibm-smartcloud.apiversion>
+                                        <test.ibm-smartcloud.api-version>${test.ibm-smartcloud.api-version}</test.ibm-smartcloud.api-version>
+                                        <test.ibm-smartcloud.build-version>${test.ibm-smartcloud.build-version}</test.ibm-smartcloud.build-version>
                                         <test.ibm-smartcloud.identity>${test.ibm-smartcloud.identity}</test.ibm-smartcloud.identity>
                                         <test.ibm-smartcloud.credential>${test.ibm-smartcloud.credential}</test.ibm-smartcloud.credential>
                                         <test.ibm-smartcloud.image-id>${test.ibm-smartcloud.image-id}</test.ibm-smartcloud.image-id>
diff --git a/sandbox-providers/ibm-smartcloud/src/test/java/org/jclouds/ibm/smartcloud/BaseIBMSmartCloudClientLiveTest.java b/sandbox-providers/ibm-smartcloud/src/test/java/org/jclouds/ibm/smartcloud/BaseIBMSmartCloudClientLiveTest.java
index 736bf85..b755e69 100644
--- a/sandbox-providers/ibm-smartcloud/src/test/java/org/jclouds/ibm/smartcloud/BaseIBMSmartCloudClientLiveTest.java
+++ b/sandbox-providers/ibm-smartcloud/src/test/java/org/jclouds/ibm/smartcloud/BaseIBMSmartCloudClientLiveTest.java
@@ -49,7 +49,7 @@
    protected String identity;
    protected String credential;
    protected String endpoint;
-   protected String apiversion;
+   protected String apiVersion;
 
    protected IBMSmartCloudClient connection;
 
@@ -61,7 +61,7 @@
       credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider
                + ".credential must be set.  ex. secretKey");
       endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+     apiVersion = System.getProperty("test." + provider + ".api-version");
    }
 
    protected Properties setupProperties() {
@@ -72,8 +72,8 @@
       overrides.setProperty(provider + ".credential", credential);
       if (endpoint != null)
          overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
+      if (apiVersion != null)
+         overrides.setProperty(provider + ".api-version", apiVersion);
       return overrides;
    }
 
diff --git a/sandbox-providers/scaleup-storage/pom.xml b/sandbox-providers/scaleup-storage/pom.xml
index cc711d8..9840623 100644
--- a/sandbox-providers/scaleup-storage/pom.xml
+++ b/sandbox-providers/scaleup-storage/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.scaleup.storage.blobstore.ScaleUpStorageTestInitializer</test.initializer>
         <test.scaleup-storage.endpoint>https://scs.scaleupstorage.com</test.scaleup-storage.endpoint>
-        <test.scaleup-storage.apiversion>2006-03-01</test.scaleup-storage.apiversion>
+        <test.scaleup-storage.api-version>2006-03-01</test.scaleup-storage.api-version>
+        <test.scaleup-storage.build-version></test.scaleup-storage.build-version>
         <test.scaleup-storage.identity>FIX_ME</test.scaleup-storage.identity>
         <test.scaleup-storage.credential>FIX_ME</test.scaleup-storage.credential>
     </properties>
@@ -108,7 +109,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.scaleup-storage.endpoint>${test.scaleup-storage.endpoint}</test.scaleup-storage.endpoint>
-                                        <test.scaleup-storage.apiversion>${test.scaleup-storage.apiversion}</test.scaleup-storage.apiversion>
+                                        <test.scaleup-storage.api-version>${test.scaleup-storage.api-version}</test.scaleup-storage.api-version>
+                                        <test.scaleup-storage.build-version>${test.scaleup-storage.build-version}</test.scaleup-storage.build-version>
                                         <test.scaleup-storage.identity>${test.scaleup-storage.identity}</test.scaleup-storage.identity>
                                         <test.scaleup-storage.credential>${test.scaleup-storage.credential}</test.scaleup-storage.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/sandbox-providers/tiscali-storage/pom.xml b/sandbox-providers/tiscali-storage/pom.xml
index 90da860..2deac1f 100644
--- a/sandbox-providers/tiscali-storage/pom.xml
+++ b/sandbox-providers/tiscali-storage/pom.xml
@@ -36,7 +36,8 @@
     <properties>
         <test.initializer>org.jclouds.tiscali.storage.blobstore.TiscaliStorageTestInitializer</test.initializer>
         <test.tiscali-storage.endpoint>https://storage.tiscali.it</test.tiscali-storage.endpoint>
-        <test.tiscali-storage.apiversion>2006-03-01</test.tiscali-storage.apiversion>
+        <test.tiscali-storage.api-version>2006-03-01</test.tiscali-storage.api-version>
+        <test.tiscali-storage.build-version></test.tiscali-storage.build-version>
         <test.tiscali-storage.identity>FIX_ME</test.tiscali-storage.identity>
         <test.tiscali-storage.credential>FIX_ME</test.tiscali-storage.credential>
     </properties>
@@ -109,7 +110,8 @@
                                     <threadCount>1</threadCount>
                                     <systemPropertyVariables>
                                         <test.tiscali-storage.endpoint>${test.tiscali-storage.endpoint}</test.tiscali-storage.endpoint>
-                                        <test.tiscali-storage.apiversion>${test.tiscali-storage.apiversion}</test.tiscali-storage.apiversion>
+                                        <test.tiscali-storage.api-version>${test.tiscali-storage.api-version}</test.tiscali-storage.api-version>
+                                        <test.tiscali-storage.build-version>${test.tiscali-storage.build-version}</test.tiscali-storage.build-version>
                                         <test.tiscali-storage.identity>${test.tiscali-storage.identity}</test.tiscali-storage.identity>
                                         <test.tiscali-storage.credential>${test.tiscali-storage.credential}</test.tiscali-storage.credential>
                                         <test.initializer>${test.initializer}</test.initializer>
diff --git a/sandbox-providers/twitter/pom.xml b/sandbox-providers/twitter/pom.xml
index b17df97..cdfcb46 100644
--- a/sandbox-providers/twitter/pom.xml
+++ b/sandbox-providers/twitter/pom.xml
@@ -50,7 +50,8 @@
     <properties>
         <!-- when instances are hung, open a ticket and add here -->
         <test.twitter.endpoint>http://api.twitter.com</test.twitter.endpoint>
-        <test.twitter.apiversion>1.0</test.twitter.apiversion>
+        <test.twitter.api-version>1.0</test.twitter.api-version>
+        <test.twitter.build-version></test.twitter.build-version>
         <test.twitter.identity>FIXME</test.twitter.identity>
         <test.twitter.credential>FIXME</test.twitter.credential>
     </properties>
@@ -98,7 +99,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.twitter.endpoint>${test.twitter.endpoint}</test.twitter.endpoint>
-                                        <test.twitter.apiversion>${test.twitter.apiversion}</test.twitter.apiversion>
+                                        <test.twitter.api-version>${test.twitter.api-version}</test.twitter.api-version>
+                                        <test.twitter.build-version>${test.twitter.build-version}</test.twitter.build-version>
                                         <test.twitter.identity>${test.twitter.identity}</test.twitter.identity>
                                         <test.twitter.credential>${test.twitter.credential}</test.twitter.credential>
                                     </systemPropertyVariables>
diff --git a/sandbox-providers/twitter/src/test/java/org/jclouds/twitter/TwitterClientLiveTest.java b/sandbox-providers/twitter/src/test/java/org/jclouds/twitter/TwitterClientLiveTest.java
index 30ef89b..0ac3cf8 100644
--- a/sandbox-providers/twitter/src/test/java/org/jclouds/twitter/TwitterClientLiveTest.java
+++ b/sandbox-providers/twitter/src/test/java/org/jclouds/twitter/TwitterClientLiveTest.java
@@ -51,13 +51,13 @@
    protected String identity;
    protected String credential;
    protected String endpoint;
-   protected String apiversion;
+   protected String apiVersion;
 
    protected void setupCredentials() {
       identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
       credential = System.getProperty("test." + provider + ".credential");
       endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+     apiVersion = System.getProperty("test." + provider + ".api-version");
    }
 
    protected Properties setupProperties() {
@@ -69,8 +69,8 @@
          overrides.setProperty(provider + ".credential", credential);
       if (endpoint != null)
          overrides.setProperty(provider + ".endpoint", endpoint);
-      if (apiversion != null)
-         overrides.setProperty(provider + ".apiversion", apiversion);
+      if (apiVersion != null)
+         overrides.setProperty(provider + ".api-version", apiVersion);
       return overrides;
    }
 
diff --git a/sandbox-providers/virtacore-vcloudexpress/pom.xml b/sandbox-providers/virtacore-vcloudexpress/pom.xml
index 37b4df5..a2a0d29 100644
--- a/sandbox-providers/virtacore-vcloudexpress/pom.xml
+++ b/sandbox-providers/virtacore-vcloudexpress/pom.xml
@@ -34,7 +34,8 @@
 
     <properties>
         <test.virtacore-vcloudexpress.endpoint>https://vcloud.virtacore.com/api</test.virtacore-vcloudexpress.endpoint>
-        <test.virtacore-vcloudexpress.apiversion>1.0</test.virtacore-vcloudexpress.apiversion>
+        <test.virtacore-vcloudexpress.api-version>1.0</test.virtacore-vcloudexpress.api-version>
+        <test.virtacore-vcloudexpress.build-version></test.virtacore-vcloudexpress.build-version>
         <test.virtacore-vcloudexpress.identity>FIXME_IDENTITY</test.virtacore-vcloudexpress.identity>
         <test.virtacore-vcloudexpress.credential>FIXME_CREDENTIAL</test.virtacore-vcloudexpress.credential>
         <test.virtacore-vcloudexpress.image-id></test.virtacore-vcloudexpress.image-id>
@@ -99,7 +100,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.virtacore-vcloudexpress.endpoint>${test.virtacore-vcloudexpress.endpoint}</test.virtacore-vcloudexpress.endpoint>
-                                        <test.virtacore-vcloudexpress.apiversion>${test.virtacore-vcloudexpress.apiversion}</test.virtacore-vcloudexpress.apiversion>
+                                        <test.virtacore-vcloudexpress.api-version>${test.virtacore-vcloudexpress.api-version}</test.virtacore-vcloudexpress.api-version>
+                                        <test.virtacore-vcloudexpress.build-version>${test.virtacore-vcloudexpress.build-version}</test.virtacore-vcloudexpress.build-version>
                                         <test.virtacore-vcloudexpress.identity>${test.virtacore-vcloudexpress.identity}</test.virtacore-vcloudexpress.identity>
                                         <test.virtacore-vcloudexpress.credential>${test.virtacore-vcloudexpress.credential}</test.virtacore-vcloudexpress.credential>
                                         <test.virtacore-vcloudexpress.image-id>${test.virtacore-vcloudexpress.image-id}</test.virtacore-vcloudexpress.image-id>
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKey.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKey.java
deleted file mode 100644
index e9d42c8..0000000
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AuthorizeRSAPublicKey.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * 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.scriptbuilder.domain;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys;
-
-import com.google.common.collect.ImmutableSet;
-
-/**
- * 
- * @author Adrian Cole
- * @see AuthorizeRSAPublicKeys
- */
-@Deprecated
-public class AuthorizeRSAPublicKey extends AuthorizeRSAPublicKeys {
-
-   public AuthorizeRSAPublicKey(String publicKey) {
-      super(ImmutableSet.of(checkNotNull(publicKey, "publicKey")));
-   }
-
-}
\ No newline at end of file
diff --git a/skeletons/standalone-compute/pom.xml b/skeletons/standalone-compute/pom.xml
index c416987..e3dd9b9 100644
--- a/skeletons/standalone-compute/pom.xml
+++ b/skeletons/standalone-compute/pom.xml
@@ -37,7 +37,8 @@
         <jclouds.compute.blacklist-nodes>trmkrun-ccc,test.trmk-924</jclouds.compute.blacklist-nodes>
         <test.servermanager.identity>FIXME</test.servermanager.identity>
         <test.servermanager.endpoint>https://servermanager.com</test.servermanager.endpoint>
-        <test.servermanager.apiversion>1.0</test.servermanager.apiversion>
+        <test.servermanager.api-version>1.0</test.servermanager.api-version>
+        <test.servermanager.build-version></test.servermanager.build-version>
         <test.servermanager.identity>FIXME</test.servermanager.identity>
     </properties>
     <dependencies>
@@ -98,7 +99,8 @@
                                 <configuration>
                                     <systemPropertyVariables>
                                         <test.servermanager.endpoint>${test.servermanager.endpoint}</test.servermanager.endpoint>
-                                        <test.servermanager.apiversion>${test.servermanager.apiversion}</test.servermanager.apiversion>
+                                        <test.servermanager.api-version>${test.servermanager.api-version}</test.servermanager.api-version>
+                                        <test.servermanager.build-version>${test.servermanager.build-version}</test.servermanager.build-version>
                                         <test.servermanager.identity>${test.servermanager.identity}</test.servermanager.identity>
                                         <jclouds.compute.blacklist-nodes>${jclouds.compute.blacklist-nodes}</jclouds.compute.blacklist-nodes>
                                     </systemPropertyVariables>
diff --git a/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceContextBuilderTest.java b/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceContextBuilderTest.java
index 3aea091..9992f1a 100644
--- a/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceContextBuilderTest.java
+++ b/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceContextBuilderTest.java
@@ -55,7 +55,7 @@
    public void testCanBuildWithContextSpec() {
       ComputeServiceContext context = new ComputeServiceContextFactory()
                .createContext(new StandaloneComputeServiceContextSpec<ServerManager, Server, Hardware, Image, Datacenter>(
-                        "servermanager", "http://host", "1", "", "identity", "credential", ServerManager.class,
+                        "servermanager", "http://host", "1", "", "", "identity", "credential", ServerManager.class,
                         ServerManagerComputeServiceContextBuilder.class, ImmutableSet.<Module> of()));
 
       context.close();
@@ -67,7 +67,7 @@
       restProperties.setProperty("servermanager.contextbuilder", ServerManagerComputeServiceContextBuilder.class
                .getName());
       restProperties.setProperty("servermanager.endpoint", "http://host");
-      restProperties.setProperty("servermanager.apiversion", "1");
+      restProperties.setProperty("servermanager.api-version", "1");
 
       ComputeServiceContext context = new ComputeServiceContextFactory(restProperties).createContext("servermanager",
                "identity", "credential");
diff --git a/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceLiveTest.java b/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceLiveTest.java
index 51c5278..7844e19 100644
--- a/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceLiveTest.java
+++ b/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceLiveTest.java
@@ -50,7 +50,7 @@
       restProperties.setProperty("servermanager.contextbuilder",
             ServerManagerComputeServiceContextBuilder.class.getName());
       restProperties.setProperty("servermanager.endpoint", "http://host");
-      restProperties.setProperty("servermanager.apiversion", "1");
+      restProperties.setProperty("servermanager.api-version", "1");
       return restProperties;
    }
 
diff --git a/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerExperimentLiveTest.java b/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerExperimentLiveTest.java
index e0753bb..aa547e9 100644
--- a/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerExperimentLiveTest.java
+++ b/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerExperimentLiveTest.java
@@ -18,8 +18,7 @@
  */
 package org.jclouds.servermanager.compute;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
+import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.ComputeServiceContextFactory;
 import org.jclouds.compute.StandaloneComputeServiceContextSpec;
@@ -28,7 +27,6 @@
 import org.jclouds.servermanager.Image;
 import org.jclouds.servermanager.Server;
 import org.jclouds.servermanager.ServerManager;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableSet;
@@ -38,29 +36,19 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live")
-public class ServerManagerExperimentLiveTest {
-   protected String provider = "servermanager";
-   protected String identity;
-   protected String credential;
-   protected String endpoint;
-   protected String apiversion;
-
-   @BeforeClass
-   protected void setupCredentials() {
-      identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
-      credential = System.getProperty("test." + provider + ".credential");
-      endpoint = System.getProperty("test." + provider + ".endpoint");
-      apiversion = System.getProperty("test." + provider + ".apiversion");
+@Test(groups = "live", singleThreaded = true, testName = "ServerManagerExperimentLiveTest")
+public class ServerManagerExperimentLiveTest extends BaseVersionedServiceLiveTest {
+   public ServerManagerExperimentLiveTest() {
+      provider = "servermanager";
    }
-
+   
    @Test
    public void testAndExperiment() {
       ComputeServiceContext context = null;
       try {
          context = new ComputeServiceContextFactory()
                   .createContext(new StandaloneComputeServiceContextSpec<ServerManager, Server, Hardware, Image, Datacenter>(
-                           "servermanager", endpoint, apiversion, "", identity, credential, ServerManager.class,
+                           "servermanager", endpoint, apiVersion, buildVersion, "", identity, credential, ServerManager.class,
                            ServerManagerComputeServiceContextBuilder.class, ImmutableSet.<Module> of()));
 
          context.getComputeService().listNodes();
@@ -71,4 +59,4 @@
       }
    }
 
-}
\ No newline at end of file
+}