merge
diff --git a/apis/cloudservers/pom.xml b/apis/cloudservers/pom.xml
index 5eea292..41a3c0a 100644
--- a/apis/cloudservers/pom.xml
+++ b/apis/cloudservers/pom.xml
@@ -37,12 +37,10 @@
   <properties>
     <test.cloudservers.endpoint>https://auth.api.rackspacecloud.com</test.cloudservers.endpoint>
     <test.cloudservers.api-version>1.0</test.cloudservers.api-version>
-    <test.cloudservers.build-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 />
-    <test.cloudservers.image.login-user />
-    <test.cloudservers.image.authenticate-sudo />
+    <test.cloudservers.template></test.cloudservers.template>
     <jclouds.osgi.export>org.jclouds.cloudservers*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -120,10 +118,8 @@
                     <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>
-		    <test.cloudstack.image.login-user>${test.cloudstack.image.login-user}</test.cloudstack.image.login-user>
-                    <test.cloudstack.image.authenticate-sudo>${test.cloudstack.image.authenticate-sudo}</test.cloudstack.image.authenticate-sudo>
-                  </systemPropertyVariables>
+                    <test.cloudstack.template>${test.cloudstack.template}</test.cloudstack.template>
+		                  </systemPropertyVariables>
                 </configuration>
               </execution>
             </executions>
diff --git a/apis/cloudsigma/pom.xml b/apis/cloudsigma/pom.xml
index 454ea1f..d36ea17 100644
--- a/apis/cloudsigma/pom.xml
+++ b/apis/cloudsigma/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.cloudsigma.endpoint>https://api.cloudsigma.com</test.cloudsigma.endpoint>
     <test.cloudsigma.api-version>1.0</test.cloudsigma.api-version>
-    <test.cloudsigma.build-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>f3c7c665-cd54-4a78-8fd2-7ec2f028cf29</test.cloudsigma.image-id>
-    <test.cloudsigma.image.login-user />
-    <test.cloudsigma.image.authenticate-sudo />
-
+    <test.cloudsigma.template>imageId=f3c7c665-cd54-4a78-8fd2-7ec2f028cf29</test.cloudsigma.template>
       <jclouds.osgi.export>org.jclouds.cloudsigma*;version="${project.version}"</jclouds.osgi.export>
       <jclouds.osgi.import>
           org.jclouds.compute.internal;version="${project.version}",
@@ -107,9 +104,7 @@
                     <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>
-                    <test.cloudsigma.image.login-user>${test.cloudsigma.image.login-user}</test.cloudsigma.image.login-user>
-                    <test.cloudsigma.image.authenticate-sudo>${test.cloudsigma.image.authenticate-sudo}</test.cloudsigma.image.authenticate-sudo>
+                    <test.cloudsigma.template>${test.cloudsigma.template}</test.cloudsigma.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
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 1a639fc..36f788d 100644
--- a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java
+++ b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java
@@ -57,7 +57,6 @@
 
 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.common.net.HostAndPort;
@@ -84,6 +83,7 @@
    protected Predicate<HostAndPort> socketTester;
 
    protected Predicate<DriveInfo> driveNotClaimed;
+   protected String imageId;
    
    @BeforeGroups(groups = { "integration", "live" })
    @Override
@@ -97,7 +97,7 @@
       socketTester = new RetryablePredicate<HostAndPort>(new InetSocketAddressConnect(), maxDriveImageTime, 1,
             TimeUnit.SECONDS);
 
-      if (Strings.emptyToNull(imageId) == null) {
+      if (template == null || template.getImageId() == null) {
          imageId = view.getComputeService().templateBuilder().build().getImage().getId();
       }
    }
diff --git a/apis/cloudstack/pom.xml b/apis/cloudstack/pom.xml
index a61c690..c711e8d 100644
--- a/apis/cloudstack/pom.xml
+++ b/apis/cloudstack/pom.xml
@@ -49,18 +49,15 @@
   
   <properties>
     <test.cloudstack.endpoint>http://localhost:8080/client/api</test.cloudstack.endpoint>
-    <test.cloudstack.api-version>2.2.12</test.cloudstack.api-version>
-    <test.cloudstack.build-version />
+    <test.cloudstack.api-version>3.0.3</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 />
-    <test.cloudstack.domainAdminCredential />
-    <test.cloudstack.globalAdminIdentity />
-    <test.cloudstack.globalAdminCredential />
-    <test.cloudstack.image-id />
-    <test.cloudstack.image.login-user />
-    <test.cloudstack.image.authenticate-sudo />
-
+    <test.cloudstack.domainAdminIdentity></test.cloudstack.domainAdminIdentity>
+    <test.cloudstack.domainAdminCredential></test.cloudstack.domainAdminCredential>
+    <test.cloudstack.globalAdminIdentity></test.cloudstack.globalAdminIdentity>
+    <test.cloudstack.globalAdminCredential></test.cloudstack.globalAdminCredential>
+    <test.cloudstack.template></test.cloudstack.template>
     <jclouds.osgi.export>org.jclouds.cloudstack*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -137,9 +134,7 @@
                     <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>
-                    <test.cloudstack.image.login-user>${test.cloudstack.image.login-user}</test.cloudstack.image.login-user>
-                    <test.cloudstack.image.authenticate-sudo>${test.cloudstack.image.authenticate-sudo}</test.cloudstack.image.authenticate-sudo>
+                    <test.cloudstack.template>${test.cloudstack.template}</test.cloudstack.template>
                     <test.cloudstack.domainAdminIdentity>${test.cloudstack.domainAdminIdentity}</test.cloudstack.domainAdminIdentity>
                     <test.cloudstack.domainAdminCredential>${test.cloudstack.domainAdminCredential}</test.cloudstack.domainAdminCredential>
                     <test.cloudstack.globalAdminIdentity>${test.cloudstack.globalAdminIdentity}</test.cloudstack.globalAdminIdentity>
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 d2a34b2..73e4a61 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
@@ -75,7 +75,7 @@
                }
             }));
 
-         String defaultTemplate = (imageId != null && !"".equals(imageId)) ? imageId : null;
+         String defaultTemplate = template != null ? template.getImageId() : null;
 
          vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network,
             defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()),
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 7f94a60..3fa074e 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
@@ -92,7 +92,7 @@
    public void testCreateVm() {
       if (networksDisabled)
          return;
-      String defaultTemplate = (imageId != null && !"".equals(imageId)) ? imageId : null;
+      String defaultTemplate = template != null ? template.getImageId() : null;
       vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network,
             defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()),
             client, jobComplete, virtualMachineRunning);
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 30a9b2c..4320390 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
@@ -171,7 +171,7 @@
    public void testCreateVMInSecurityGroup() throws Exception {
       if (!securityGroupsSupported)
          return;
-      String defaultTemplate = (imageId != null && !"".equals(imageId)) ? imageId : null;
+      String defaultTemplate = template != null ? template.getImageId() : null;
       vm = VirtualMachineClientLiveTest.createVirtualMachineWithSecurityGroupInZone(zone.getId(),
             defaultTemplateOrPreferredInZone(defaultTemplate, client, zone.getId()), group.getId(), client,
             jobComplete, virtualMachineRunning);
@@ -200,7 +200,7 @@
    public void testCreateVMWithoutSecurityGroupAssignsDefault() throws Exception {
       if (!securityGroupsSupported)
          return;
-      String defaultTemplate = (imageId != null && !"".equals(imageId)) ? imageId : null;
+      String defaultTemplate = template != null ? template.getImageId() : null;
       VirtualMachine newVm = VirtualMachineClientLiveTest.createVirtualMachineWithOptionsInZone(DeployVirtualMachineOptions.NONE,
             zone.getId(), defaultTemplateOrPreferredInZone(defaultTemplate, client, zone.getId()), client,
             jobComplete, virtualMachineRunning);
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
index 2df4cc8..f2a9fd5 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
@@ -115,8 +115,8 @@
       assertNotNull(network);
 
       // Create a VM and stop it
-      String templateId = (imageId != null && !"".equals(imageId)) ? imageId : null;
-      vmForCreation = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network, templateId, client, jobComplete, virtualMachineRunning);
+      String defaultTemplate = template != null ? template.getImageId() : null;
+      vmForCreation = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network, defaultTemplate, client, jobComplete, virtualMachineRunning);
       assertTrue(jobComplete.apply(client.getVirtualMachineClient().stopVirtualMachine(vmForCreation.getId())), vmForCreation.toString());
 
       // Work out the VM's volume
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
index fd2a37c..dc0b4fa 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
@@ -181,8 +181,8 @@
 
    @Test
    public void testCreateVirtualMachine() throws Exception {
-      String templateId = (imageId != null && !"".equals(imageId)) ? imageId : null;
-      vm = createVirtualMachine(client, templateId, jobComplete, virtualMachineRunning);
+      String defaultTemplate = template != null ? template.getImageId() : null;
+      vm = createVirtualMachine(client, defaultTemplate, jobComplete, virtualMachineRunning);
       if (vm.getPassword() != null) {
          conditionallyCheckSSH();
       }
@@ -195,12 +195,12 @@
    public void testCreateVirtualMachineWithSpecificIp() throws Exception {
       skipIfNotGlobalAdmin();
 
-      String templateId = (imageId != null && !"".equals(imageId)) ? imageId : null;
+      String defaultTemplate = template != null ? template.getImageId() : null;
       Network network = null;
 
       try {
          Template template = getOnlyElement(
-            client.getTemplateClient().listTemplates(ListTemplatesOptions.Builder.id(templateId)));
+            client.getTemplateClient().listTemplates(ListTemplatesOptions.Builder.id(defaultTemplate)));
          logger.info("Using template: " + template);
 
          Set<Network> allSafeNetworksInZone = adminClient.getNetworkClient().listNetworks(
@@ -252,7 +252,7 @@
          ipsToNetworks.put(ipAddress, network.getId());
 
          vm = createVirtualMachineInNetworkWithIp(
-            adminClient, templateId, ImmutableSet.of(requiredNetwork, network),
+            adminClient, defaultTemplate, ImmutableSet.of(requiredNetwork, network),
             ipsToNetworks, adminJobComplete, adminVirtualMachineRunning);
          logger.info("Created VM: " + vm);
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
index 6298852..0710ae3 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
@@ -64,7 +64,7 @@
       prefix += "nat";
       try {
          network = find(client.getNetworkClient().listNetworks(), NetworkPredicates.supportsStaticNAT());
-         String defaultTemplate = (imageId != null && !"".equals(imageId)) ? imageId : null;
+         String defaultTemplate = template != null ? template.getImageId() : null;
          vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network,
                defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()), client, jobComplete,
                virtualMachineRunning);
diff --git a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchAsyncClientTest.java b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchAsyncClientTest.java
index d313ccd..8f730d6 100644
--- a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchAsyncClientTest.java
+++ b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchAsyncClientTest.java
@@ -27,9 +27,6 @@
 import java.util.Date;
 import java.util.Map;
 
-import javax.inject.Named;
-
-import org.jclouds.Constants;
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.aws.domain.Region;
 import org.jclouds.aws.filters.FormSigner;
@@ -111,8 +108,7 @@
       }
 
       @Override
-      protected String provideTimeStamp(final DateService dateService,
-            @Named(Constants.PROPERTY_SESSION_INTERVAL) int expiration) {
+      protected String provideTimeStamp(final DateService dateService) {
          return "2009-11-08T15:54:08.897Z";
       }
    }
diff --git a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/internal/BaseCloudWatchExpectTest.java b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/internal/BaseCloudWatchExpectTest.java
index d4576ac..765f423 100644
--- a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/internal/BaseCloudWatchExpectTest.java
+++ b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/internal/BaseCloudWatchExpectTest.java
@@ -23,9 +23,6 @@
 import java.net.URI;
 import java.util.Map;
 
-import javax.inject.Named;
-
-import org.jclouds.Constants;
 import org.jclouds.aws.domain.Region;
 import org.jclouds.cloudwatch.config.CloudWatchRestClientModule;
 import org.jclouds.date.DateService;
@@ -69,8 +66,7 @@
       }
 
       @Override
-      protected String provideTimeStamp(final DateService dateService,
-            @Named(Constants.PROPERTY_SESSION_INTERVAL) int expiration) {
+      protected String provideTimeStamp(final DateService dateService) {
          return "2009-11-08T15:54:08.897Z";
       }
    }
diff --git a/apis/deltacloud/pom.xml b/apis/deltacloud/pom.xml
index 2c32c61..bfef9b8 100644
--- a/apis/deltacloud/pom.xml
+++ b/apis/deltacloud/pom.xml
@@ -50,13 +50,10 @@
   <properties>
     <test.deltacloud.endpoint>http://localhost:3001/api</test.deltacloud.endpoint>
     <test.deltacloud.api-version>0.3.0</test.deltacloud.api-version>
-    <test.deltacloud.build-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 />
-    <test.deltacloud.image.login-user />
-    <test.deltacloud.image.authenticate-sudo />
-
+    <test.deltacloud.template></test.deltacloud.template>
     <jclouds.osgi.export>org.jclouds.deltacloud*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -120,9 +117,7 @@
                     <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>
-                    <test.deltacloud.image.login-user>${test.deltacloud.image.login-user}</test.deltacloud.image.login-user>
-                    <test.deltacloud.image.authenticate-sudo>${test.deltacloud.image.authenticate-sudo}</test.deltacloud.image.authenticate-sudo>
+                    <test.deltacloud.template>${test.deltacloud.template}</test.deltacloud.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/apis/ec2/pom.xml b/apis/ec2/pom.xml
index 11e1e06..d7d6073 100644
--- a/apis/ec2/pom.xml
+++ b/apis/ec2/pom.xml
@@ -36,13 +36,11 @@
   <properties>
     <test.ec2.endpoint>https://ec2.us-east-1.amazonaws.com</test.ec2.endpoint>
     <test.ec2.api-version>2010-06-15</test.ec2.api-version>
-    <test.ec2.build-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 />
-    <test.ec2.image.login-user />
-    <test.ec2.image.authenticate-sudo />
-
+    <test.ec2.template></test.ec2.template>
+    <test.ec2.ebs-template>hardwareId=m1.small,imageId=us-west-2/ami-38c64a08</test.ec2.ebs-template>
     <jclouds.osgi.export>org.jclouds.ec2*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -128,9 +126,8 @@
                     <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>
-                    <test.ec2.image.login-user>${test.ec2.image.login-user}</test.ec2.image.login-user>
-                    <test.ec2.image.authenticate-sudo>${test.ec2.image.authenticate-sudo}</test.ec2.image.authenticate-sudo>
+                    <test.ec2.template>${test.ec2.template}</test.ec2.template>
+                    <test.ec2.ebs-template>${test.ec2.ebs-template}</test.ec2.ebs-template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java
index dfbf062..fc97830 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/EC2ImageParser.java
@@ -32,10 +32,10 @@
 
 import org.jclouds.collect.Memoized;
 import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.Image.Status;
 import org.jclouds.compute.domain.ImageBuilder;
 import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.OsFamily;
-import org.jclouds.compute.domain.Image.Status;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy;
 import org.jclouds.compute.util.ComputeServiceUtils;
@@ -100,7 +100,7 @@
 
       OperatingSystem.Builder osBuilder = OperatingSystem.builder();
       osBuilder.is64Bit(from.getArchitecture() == Architecture.X86_64);
-      OsFamily family = parseOsFamilyOrUnrecognized(from.getImageLocation());
+      OsFamily family = parseOsFamily(from);
       osBuilder.family(family);
       osBuilder.version(ComputeServiceUtils.parseVersionOrReturnEmptyString(family, from.getImageLocation(),
                osVersionMap));
@@ -131,4 +131,20 @@
       return builder.build();
    }
 
+   /** 
+    * First treats windows as a special case: check if platform==windows.
+    * Then tries matching based on the image name.
+    * And then falls back to checking other types of platform.
+    */
+   private OsFamily parseOsFamily(org.jclouds.ec2.domain.Image from) {
+      if (from.getPlatform() != null && from.getPlatform().equalsIgnoreCase("windows")) {
+         return OsFamily.WINDOWS;
+      }
+      
+      OsFamily family = parseOsFamilyOrUnrecognized(from.getImageLocation());
+      if (family == OsFamily.UNRECOGNIZED && from.getPlatform() != null) {
+         family = parseOsFamilyOrUnrecognized(from.getPlatform());
+      }
+      return family;
+   }
 }
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/config/EC2RestClientModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/config/EC2RestClientModule.java
index 48047b3..264b012 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/config/EC2RestClientModule.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/config/EC2RestClientModule.java
@@ -40,11 +40,15 @@
 import org.jclouds.ec2.services.WindowsAsyncClient;
 import org.jclouds.ec2.services.WindowsClient;
 import org.jclouds.ec2.suppliers.DescribeAvailabilityZonesInRegion;
-import org.jclouds.ec2.suppliers.DescribeRegionsForConfiguredRegions;
+import org.jclouds.ec2.suppliers.DescribeRegionsForRegionURIs;
 import org.jclouds.location.config.LocationModule;
 import org.jclouds.location.suppliers.RegionIdToURISupplier;
 import org.jclouds.location.suppliers.RegionIdToZoneIdsSupplier;
+import org.jclouds.location.suppliers.RegionIdsSupplier;
+import org.jclouds.location.suppliers.ZoneIdToURISupplier;
 import org.jclouds.location.suppliers.ZoneIdsSupplier;
+import org.jclouds.location.suppliers.derived.RegionIdsFromRegionIdToURIKeySet;
+import org.jclouds.location.suppliers.derived.ZoneIdToURIFromJoinOnRegionIdToURI;
 import org.jclouds.location.suppliers.derived.ZoneIdsFromRegionIdToZoneIdsValues;
 import org.jclouds.rest.ConfiguresRestClient;
 
@@ -81,13 +85,13 @@
       super(syncClientType, asyncClientType, sync2Async);
    }
    
-
-
    @Override
    protected void installLocations() {
       install(new LocationModule());
       bind(RegionIdToZoneIdsSupplier.class).to(DescribeAvailabilityZonesInRegion.class).in(Scopes.SINGLETON);
-      bind(RegionIdToURISupplier.class).to(DescribeRegionsForConfiguredRegions.class).in(Scopes.SINGLETON);
+      bind(RegionIdToURISupplier.class).to(DescribeRegionsForRegionURIs.class).in(Scopes.SINGLETON);
       bind(ZoneIdsSupplier.class).to(ZoneIdsFromRegionIdToZoneIdsValues.class).in(Scopes.SINGLETON);
+      bind(RegionIdsSupplier.class).to(RegionIdsFromRegionIdToURIKeySet.class).in(Scopes.SINGLETON);
+      bind(ZoneIdToURISupplier.class).to(ZoneIdToURIFromJoinOnRegionIdToURI.class).in(Scopes.SINGLETON);
    }
 }
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java
index a199231..0a11cb6 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/RunningInstance.java
@@ -269,9 +269,9 @@
       this.dnsName = dnsName; // nullable on runinstances.
       this.imageId = imageId; // nullable on runinstances.
       this.instanceId = checkNotNull(instanceId, "instanceId");
-      this.instanceState = checkNotNull(instanceState, "instanceState");
-      this.rawState = checkNotNull(rawState, "rawState");
-      this.instanceType = checkNotNull(instanceType, "instanceType");
+      this.instanceState = checkNotNull(instanceState, "instanceState for %s/%s", region, instanceId);
+      this.rawState = checkNotNull(rawState, "rawState for %s/%s", region, instanceId);
+      this.instanceType = checkNotNull(instanceType, "instanceType for %s/%s", region, instanceId);
       this.ipAddress = ipAddress;
       this.kernelId = kernelId;
       this.keyName = keyName;
@@ -283,10 +283,10 @@
       this.privateIpAddress = privateIpAddress;// nullable on runinstances.
       this.ramdiskId = ramdiskId;
       this.reason = reason;
-      this.rootDeviceType = checkNotNull(rootDeviceType, "rootDeviceType");
+      this.rootDeviceType = checkNotNull(rootDeviceType, "rootDeviceType for %s/%s", region, instanceId);
       this.rootDeviceName = rootDeviceName;
-      this.ebsBlockDevices = ImmutableMap.copyOf(checkNotNull(ebsBlockDevices, "ebsBlockDevices"));
-      this.groupIds = ImmutableSet.copyOf(checkNotNull(groupIds, "groupIds"));
+      this.ebsBlockDevices = ImmutableMap.copyOf(checkNotNull(ebsBlockDevices, "ebsBlockDevices for %s/%s", region, instanceId));
+      this.groupIds = ImmutableSet.copyOf(checkNotNull(groupIds, "groupIds for %s/%s", region, instanceId));
    }
 
    /**
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java
index 14f003f..427560b 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java
@@ -97,7 +97,7 @@
       private String availabilityZone;
       private Status status;
       private Date createTime;
-      private Set<Attachment> attachments;
+      private Set<Attachment> attachments = ImmutableSet.of();
 
       public Builder region(String region) {
          this.region = region;
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceAsyncClient.java
index 96d361e..62019a2 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/InstanceAsyncClient.java
@@ -114,6 +114,7 @@
    @Path("/")
    @FormParams(keys = ACTION, values = "TerminateInstances")
    @XMLResponseParser(InstanceStateChangeHandler.class)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
    ListenableFuture<Set<? extends InstanceStateChange>> terminateInstancesInRegion(
          @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region,
          @BinderParam(BindInstanceIdsToIndexedFormParams.class) String... instanceIds);
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/suppliers/DescribeRegionsForConfiguredRegions.java b/apis/ec2/src/main/java/org/jclouds/ec2/suppliers/DescribeRegionsForRegionURIs.java
similarity index 76%
rename from apis/ec2/src/main/java/org/jclouds/ec2/suppliers/DescribeRegionsForConfiguredRegions.java
rename to apis/ec2/src/main/java/org/jclouds/ec2/suppliers/DescribeRegionsForRegionURIs.java
index 6e3d7ab..b802aeb 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/suppliers/DescribeRegionsForConfiguredRegions.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/suppliers/DescribeRegionsForRegionURIs.java
@@ -20,7 +20,6 @@
 
 import java.net.URI;
 import java.util.Map;
-import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -31,29 +30,23 @@
 import org.jclouds.location.suppliers.RegionIdToURISupplier;
 import org.jclouds.util.Suppliers2;
 
-import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
 import com.google.common.collect.Maps;
 
 @Singleton
-public class DescribeRegionsForConfiguredRegions implements RegionIdToURISupplier {
+public class DescribeRegionsForRegionURIs implements RegionIdToURISupplier {
    private final AvailabilityZoneAndRegionClient client;
-   private final Supplier<Set<String>> regions;
 
    @Inject
-   public DescribeRegionsForConfiguredRegions(EC2Client client, @Region Supplier<Set<String>> regions) {
+   public DescribeRegionsForRegionURIs(EC2Client client) {
       this.client = client.getAvailabilityZoneAndRegionServices();
-      this.regions = regions;
    }
 
    @Singleton
    @Region
    @Override
    public Map<String, Supplier<URI>> get() {
-      Set<String> regionWhiteList = regions.get();
       Map<String, URI> regionToUris = client.describeRegions();
-      if (regionWhiteList.size() > 0)
-         regionToUris = Maps.filterKeys(regionToUris, Predicates.in(regionWhiteList));
       return Maps.transformValues(regionToUris, Suppliers2.<URI> ofInstanceFunction());
    }
 }
\ No newline at end of file
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java
index 38fe564..807d555 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/BaseReservationHandler.java
@@ -72,7 +72,10 @@
    protected Builder builder;
 
    protected int itemDepth;
-   boolean inInstancesSet;
+   protected boolean inInstancesSet;
+   protected boolean inProductCodes;
+   protected boolean inGroupSet;
+
    // attachments
    private String volumeId;
    private Attachment.Status attachmentStatus;
@@ -88,12 +91,17 @@
 
    private Set<RunningInstance> instances = Sets.newLinkedHashSet();
 
+
    public void startElement(String uri, String name, String qName, Attributes attrs) {
       if (equalsOrSuffix(qName, "item")) {
          itemDepth++;
       } else if (equalsOrSuffix(qName, "instancesSet")) {
          inInstancesSet = true;
-      }
+      } else if (equalsOrSuffix(qName, "productCodes")) {
+         inProductCodes = true;
+      } else if (equalsOrSuffix(qName, "groupSet")) {
+         inGroupSet = true;
+      } 
    }
 
    public void endElement(String uri, String name, String qName) {
@@ -102,6 +110,10 @@
          itemDepth--;
       } else if (equalsOrSuffix(qName, "instancesSet")) {
          inInstancesSet = false;
+      } else if (equalsOrSuffix(qName, "productCodes")) {
+         inProductCodes = false;
+      } else if (equalsOrSuffix(qName, "groupSet")) {
+         inGroupSet = false;
       } else if (equalsOrSuffix(qName, "groupId")) {
          groupIds.add(currentOrNull(currentText));
       } else if (equalsOrSuffix(qName, "ownerId")) {
@@ -219,7 +231,7 @@
    }
 
    protected boolean endOfInstanceItem() {
-      return itemDepth <= 2 && inInstancesSet;
+      return itemDepth <= 2 && inInstancesSet && !inProductCodes && !inGroupSet;
    }
 
    public void characters(char ch[], int start, int length) {
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeRegionsResponseHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeRegionsResponseHandler.java
index 5d5cd37..af493eb 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeRegionsResponseHandler.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeRegionsResponseHandler.java
@@ -55,7 +55,7 @@
          String pending = currentText.toString().trim();
          if (pending.indexOf("Walrus") == -1)
             regionEndpoint = URI.create(pending.startsWith("http") ? pending : String.format("https://%s", pending));
-      } else if (qName.equals("item") && region != null) {
+      } else if (qName.equals("item") && region != null && regionEndpoint != null) {
          regionEndpoints.put(region, regionEndpoint);
          this.region = null;
          this.regionEndpoint = null;
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/BaseEC2ComputeServiceExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/BaseEC2ComputeServiceExpectTest.java
index f663fde..37867fb 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/BaseEC2ComputeServiceExpectTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/BaseEC2ComputeServiceExpectTest.java
@@ -59,8 +59,7 @@
    private static final class TestEC2RestClientModule extends EC2RestClientModule<EC2Client, EC2AsyncClient> {
       @Override
       @Provides
-      protected String provideTimeStamp(final DateService dateService,
-               @Named(Constants.PROPERTY_SESSION_INTERVAL) final int expiration) {
+      protected String provideTimeStamp(DateService dateService) {
          return CONSTANT_DATE;
       }
    }
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 f5ebb78..b204a07 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
@@ -29,8 +29,8 @@
 
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilderSpec;
 import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.predicates.NodePredicates;
@@ -41,7 +41,6 @@
 import org.jclouds.ec2.EC2Client;
 import org.jclouds.ec2.compute.options.EC2TemplateOptions;
 import org.jclouds.ec2.domain.BlockDevice;
-import org.jclouds.ec2.domain.InstanceType;
 import org.jclouds.ec2.domain.IpProtocol;
 import org.jclouds.ec2.domain.KeyPair;
 import org.jclouds.ec2.domain.PublicIpInstanceIdPair;
@@ -57,6 +56,7 @@
 import org.jclouds.scriptbuilder.domain.Statements;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.jclouds.util.InetAddresses2;
+import org.testng.SkipException;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;
@@ -74,6 +74,8 @@
 @Test(groups = "live", singleThreaded = true)
 public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest {
 
+   protected TemplateBuilderSpec ebsTemplate;
+
    public EC2ComputeServiceLiveTest() {
       provider = "ec2";
    }
@@ -227,12 +229,24 @@
       }
    }
    
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      String ebsSpec = setIfTestSystemPropertyPresent(overrides, provider + ".ebs-template");
+      if (ebsSpec != null)
+         ebsTemplate = TemplateBuilderSpec.parse(ebsSpec);
+      return overrides;
+   }
+
    /**
     * Note we cannot use the micro size as it has no ephemeral space.
     */
-   @Test(enabled = true)
+   @Test
    public void testMapEBS() throws Exception {
-
+      if (ebsTemplate == null) {
+         throw new SkipException("Test cannot run without the parameter test." + provider
+               + ".ebs-template; this property should be in the format defined in TemplateBuilderSpec");
+      }
       InstanceClient instanceClient = EC2Client.class.cast(view.unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi())
                .getInstanceServices();
 
@@ -242,8 +256,7 @@
       String group = this.group + "e";
       int volumeSize = 8;
       
-      final Template template = view.getComputeService().templateBuilder().hardwareId(InstanceType.M1_SMALL)
-               .osFamily(OsFamily.UBUNTU).osVersionMatches("10.04").imageDescriptionMatches(".*ebs.*").build();
+      final Template template = view.getComputeService().templateBuilder().from(ebsTemplate).build();
 
       Location zone = Iterables.find(view.getComputeService().listAssignableLocations(), new Predicate<Location>() {
 
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2ImageParserTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2ImageParserTest.java
index c364f1b..765659f 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2ImageParserTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/EC2ImageParserTest.java
@@ -24,10 +24,10 @@
 import java.util.Set;
 
 import org.jclouds.compute.config.BaseComputeServiceContextModule;
+import org.jclouds.compute.domain.Image.Status;
 import org.jclouds.compute.domain.ImageBuilder;
 import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.OsFamily;
-import org.jclouds.compute.domain.Image.Status;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LocationBuilder;
@@ -64,24 +64,48 @@
          assertEquals(image.getBackendStatus(), "available");
       }
       
-      assertEquals(Iterables.get(result, 0), new ImageBuilder().operatingSystem(
+      assertImageEquals(Iterables.get(result, 0), new ImageBuilder().operatingSystem(
                new OperatingSystem.Builder().family(OsFamily.UNRECOGNIZED).arch("paravirtual").version("").description(
                         "137112412989/amzn-ami-0.9.7-beta.i386-ebs").is64Bit(false).build()).description("Amazon")
                .defaultCredentials(new LoginCredentials("ec2-user", false)).id("us-east-1/ami-82e4b5c7").name(
                         "amzn-ami-0.9.7-beta.i386-ebs").providerId("ami-82e4b5c7").location(defaultLocation)
                .userMetadata(ImmutableMap.of("owner", "137112412989", "rootDeviceType", "ebs")).status(
-                        Status.AVAILABLE).build());
+                        Status.AVAILABLE).backendStatus("available").build());
 
-      assertEquals(Iterables.get(result, 3), new ImageBuilder().operatingSystem(
+      assertImageEquals(Iterables.get(result, 3), new ImageBuilder().operatingSystem(
                new OperatingSystem.Builder().family(OsFamily.UNRECOGNIZED).arch("paravirtual").version("").description(
                         "amzn-ami-us-west-1/amzn-ami-0.9.7-beta.x86_64.manifest.xml").is64Bit(true).build())
                .description("Amazon Linux AMI x86_64 S3").defaultCredentials(new LoginCredentials("ec2-user", false))
                .id("us-east-1/ami-f2e4b5b7").providerId("ami-f2e4b5b7").name("amzn-ami-0.9.7-beta.x86_64-S3").location(
                         defaultLocation)
                .userMetadata(ImmutableMap.of("owner", "137112412989", "rootDeviceType", "ebs")).status(
-                        Status.AVAILABLE).build());
+                        Status.AVAILABLE).backendStatus("available").build());
+      
+      assertImageEquals(Iterables.get(result, 4), new ImageBuilder().operatingSystem(
+               new OperatingSystem.Builder().family(OsFamily.WINDOWS).arch("hvm").version("2008").description(
+                        "amazon/NameGivesNoClue_Server-2008-R2_SP1-Language_Packs-64Bit-Base-2012.05.10")
+                        .is64Bit(true).build())
+               .description("Microsoft Windows Server 2008 R2 SP1 Datacenter 64-bit Multi-language AMI provided by Amazon")
+               .defaultCredentials(new LoginCredentials("root", false))
+               .id("us-east-1/ami-85457ff1").providerId("ami-85457ff1").name("NameGivesNoClue_Server-2008-R2_SP1-Language_Packs-64Bit-Base-2012.05.10")
+                        .location(defaultLocation)
+               .userMetadata(ImmutableMap.of("owner", "801119661308", "rootDeviceType", "ebs"))
+               .status(Status.AVAILABLE).backendStatus("available").build());
+      
    }
 
+   private void assertImageEquals(org.jclouds.compute.domain.Image actual, org.jclouds.compute.domain.Image expected) {
+      // Note that ImageImpl.equals does not compare operating system etc; there's an explicit comment:
+      // equals from super is sufficient to establish identity equivalence
+      assertEquals(actual, expected);
+      assertEquals(actual.getOperatingSystem(), expected.getOperatingSystem());
+      assertEquals(actual.getStatus(), expected.getStatus());
+      assertEquals(actual.getBackendStatus(), expected.getBackendStatus());
+      assertEquals(actual.getVersion(), expected.getVersion());
+      assertEquals(actual.getDescription(), expected.getDescription());
+      assertEquals(actual.getDefaultCredentials(), expected.getDefaultCredentials());
+   }
+   
    static Location defaultLocation = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1").description(
             "us-east-1").build();
 
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/config/EC2RestClientModuleExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/config/EC2RestClientModuleExpectTest.java
new file mode 100644
index 0000000..cee0074
--- /dev/null
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/config/EC2RestClientModuleExpectTest.java
@@ -0,0 +1,143 @@
+/**
+ * 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.config;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.jclouds.ec2.internal.BaseEC2ExpectTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.location.Region;
+import org.jclouds.location.Zone;
+import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
+import org.jclouds.location.functions.ZoneToEndpoint;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "EC2RestClientModuleExpectTest")
+public class EC2RestClientModuleExpectTest extends BaseEC2ExpectTest<Injector> {
+   private Injector injector;
+
+   public EC2RestClientModuleExpectTest() {
+      Builder<HttpRequest, HttpResponse> builder = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      builder.put(describeRegionsRequest, describeRegionsResponse);
+      builder.putAll(describeAvailabilityZonesRequestResponse);
+
+      injector = requestsSendResponses(builder.build());
+   }
+
+   public void testLocationIdAndURIBindings() {
+
+      assertEquals(injector.getInstance(Key.get(new TypeLiteral<Supplier<Set<String>>>() {
+      }, Region.class)).get(), ImmutableSet.<String> of("sa-east-1", "ap-northeast-1", "eu-west-1", "us-east-1",
+            "us-west-1", "us-west-2", "ap-southeast-1"));
+
+      assertEquals(injector.getInstance(Key.get(new TypeLiteral<Supplier<Set<String>>>() {
+      }, Zone.class)).get(), ImmutableSet.<String> of("sa-east-1a", "sa-east-1b", "ap-northeast-1a", "ap-northeast-1b",
+            "eu-west-1a", "eu-west-1b", "eu-west-1c", "us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d",
+            "us-east-1e", "us-west-1a", "us-west-1b", "us-west-1c", "us-west-2a", "us-west-2b", "us-west-2c",
+            "ap-southeast-1a", "ap-southeast-1b"));
+
+      Map<String, Supplier<URI>> regionToURISupplier = injector.getInstance(
+            Key.get(new TypeLiteral<Supplier<Map<String, Supplier<URI>>>>() {
+            }, Region.class)).get();
+
+      assertEquals(regionToURISupplier.get("sa-east-1").get(), URI.create("https://ec2.sa-east-1.amazonaws.com"));
+      assertEquals(regionToURISupplier.get("ap-northeast-1").get(),
+            URI.create("https://ec2.ap-northeast-1.amazonaws.com"));
+      assertEquals(regionToURISupplier.get("eu-west-1").get(), URI.create("https://ec2.eu-west-1.amazonaws.com"));
+      assertEquals(regionToURISupplier.get("us-east-1").get(), URI.create("https://ec2.us-east-1.amazonaws.com"));
+      assertEquals(regionToURISupplier.get("us-west-1").get(), URI.create("https://ec2.us-west-1.amazonaws.com"));
+      assertEquals(regionToURISupplier.get("us-west-2").get(), URI.create("https://ec2.us-west-2.amazonaws.com"));
+      assertEquals(regionToURISupplier.get("ap-southeast-1").get(),
+            URI.create("https://ec2.ap-southeast-1.amazonaws.com"));
+
+      Map<String, Supplier<Set<String>>> regionToZoneIdSupplier = injector.getInstance(
+            Key.get(new TypeLiteral<Supplier<Map<String, Supplier<Set<String>>>>>() {
+            }, Zone.class)).get();
+
+      assertEquals(regionToZoneIdSupplier.get("sa-east-1").get(), ImmutableSet.of("sa-east-1a", "sa-east-1b"));
+      assertEquals(regionToZoneIdSupplier.get("ap-northeast-1").get(),
+            ImmutableSet.of("ap-northeast-1a", "ap-northeast-1b"));
+      assertEquals(regionToZoneIdSupplier.get("eu-west-1").get(),
+            ImmutableSet.of("eu-west-1a", "eu-west-1b", "eu-west-1c"));
+      assertEquals(regionToZoneIdSupplier.get("us-east-1").get(),
+            ImmutableSet.of("us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d", "us-east-1e"));
+      assertEquals(regionToZoneIdSupplier.get("us-west-1").get(),
+            ImmutableSet.of("us-west-1a", "us-west-1b", "us-west-1c"));
+      assertEquals(regionToZoneIdSupplier.get("us-west-2").get(),
+            ImmutableSet.of("us-west-2a", "us-west-2b", "us-west-2c"));
+      assertEquals(regionToZoneIdSupplier.get("ap-southeast-1").get(),
+            ImmutableSet.of("ap-southeast-1a", "ap-southeast-1b"));
+
+      Map<String, Supplier<URI>> zoneToURISupplier = injector.getInstance(
+            Key.get(new TypeLiteral<Supplier<Map<String, Supplier<URI>>>>() {
+            }, Zone.class)).get();
+
+      assertEquals(zoneToURISupplier.get("sa-east-1a").get(), URI.create("https://ec2.sa-east-1.amazonaws.com"));
+
+      assertEquals(zoneToURISupplier.get("ap-northeast-1a").get(),
+            URI.create("https://ec2.ap-northeast-1.amazonaws.com"));
+
+      assertEquals(zoneToURISupplier.get("eu-west-1a").get(), URI.create("https://ec2.eu-west-1.amazonaws.com"));
+
+      assertEquals(zoneToURISupplier.get("us-east-1a").get(), URI.create("https://ec2.us-east-1.amazonaws.com"));
+
+      assertEquals(zoneToURISupplier.get("us-west-1a").get(), URI.create("https://ec2.us-west-1.amazonaws.com"));
+
+      assertEquals(zoneToURISupplier.get("us-west-2a").get(), URI.create("https://ec2.us-west-2.amazonaws.com"));
+
+      assertEquals(zoneToURISupplier.get("ap-southeast-1a").get(),
+            URI.create("https://ec2.ap-southeast-1.amazonaws.com"));
+
+   }
+
+   public void testZoneToEndpoint() {
+      assertEquals(injector.getInstance(ZoneToEndpoint.class).apply("us-west-2a"),
+            URI.create("https://ec2.us-west-2.amazonaws.com"));
+   }
+   
+   public void testRegionToEndpointOrProviderIfNull() {
+      assertEquals(injector.getInstance(RegionToEndpointOrProviderIfNull.class).apply("us-west-2"),
+            URI.create("https://ec2.us-west-2.amazonaws.com"));
+   }
+   
+   @Override
+   public Injector createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
+      return createInjector(fn, module, props);
+   }
+
+}
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/internal/BaseEC2ExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/internal/BaseEC2ExpectTest.java
new file mode 100644
index 0000000..be74eac
--- /dev/null
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/internal/BaseEC2ExpectTest.java
@@ -0,0 +1,99 @@
+/**
+ * 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.internal;
+
+import java.net.URI;
+import java.util.Map;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.aws.filters.FormSigner;
+import org.jclouds.date.DateService;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.ec2.EC2AsyncClient;
+import org.jclouds.ec2.EC2Client;
+import org.jclouds.ec2.config.EC2RestClientModule;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.internal.BaseRestClientExpectTest;
+
+import com.google.common.base.Functions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+import com.google.inject.Provides;
+
+public abstract class BaseEC2ExpectTest<T> extends BaseRestClientExpectTest<T> {
+   protected static final String CONSTANT_DATE = "2012-04-16T15:54:08.897Z";
+   
+   protected DateService dateService = new SimpleDateFormatDateService();
+   
+   protected HttpRequest describeRegionsRequest = HttpRequest
+         .builder()
+         .method("POST")
+         .endpoint(URI.create("https://ec2.us-east-1.amazonaws.com/"))
+         .headers(ImmutableMultimap.of("Host", "ec2.us-east-1.amazonaws.com"))
+         .payload(payloadFromStringWithContentType(
+                  "Action=DescribeRegions&Signature=s5OXKqaaeKhJW5FVrRntuMsUL4Ed5fjzgUWeukU96ko%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2010-06-15&AWSAccessKeyId=identity",
+                  MediaType.APPLICATION_FORM_URLENCODED)).build();
+   protected HttpResponse describeRegionsResponse = HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType("/regionEndpoints-all.xml", MediaType.APPLICATION_XML))
+         .build();
+   
+   protected final Map<HttpRequest, HttpResponse> describeAvailabilityZonesRequestResponse;
+
+   public BaseEC2ExpectTest() {
+      provider = "ec2";
+      FormSigner formSigner = createInjector(Functions.forMap(ImmutableMap.<HttpRequest, HttpResponse> of()),
+            createModule(), setupProperties()).getInstance(FormSigner.class);
+      Builder<HttpRequest, HttpResponse> builder = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      for (String region : ImmutableSet.of("ap-northeast-1", "ap-southeast-1", "eu-west-1", "sa-east-1", "us-east-1", "us-west-1", "us-west-2")){
+         builder.put(
+               formSigner.filter(HttpRequest.builder()
+                          .method("POST")
+                          .endpoint(URI.create("https://ec2." + region + ".amazonaws.com/"))
+                          .headers(ImmutableMultimap.of("Host", "ec2." + region + ".amazonaws.com"))
+                          .payload(payloadFromStringWithContentType(
+                                 "Action=DescribeAvailabilityZones&Version=2010-06-15",
+                                 MediaType.APPLICATION_FORM_URLENCODED)).build()),
+               HttpResponse.builder().statusCode(200)
+                           .payload(payloadFromResourceWithContentType(
+                                 "/availabilityZones-" + region + ".xml", MediaType.APPLICATION_XML)).build());               
+
+      }
+      describeAvailabilityZonesRequestResponse = builder.build();
+   }
+
+   @ConfiguresRestClient
+   private static final class TestEC2RestClientModule extends EC2RestClientModule<EC2Client, EC2AsyncClient> {
+      @Override
+      @Provides
+      protected String provideTimeStamp(DateService dateService) {
+         return CONSTANT_DATE;
+      }
+   }
+
+   @Override
+   protected Module createModule() {
+      return new TestEC2RestClientModule();
+   }
+}
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/AMIClientLiveTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/AMIClientLiveTest.java
new file mode 100644
index 0000000..15e112a
--- /dev/null
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/AMIClientLiveTest.java
@@ -0,0 +1,186 @@
+package org.jclouds.ec2.services;
+
+import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.imageIds;
+import static org.jclouds.ec2.options.RegisterImageBackedByEbsOptions.Builder.addNewBlockDevice;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilderSpec;
+import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
+import org.jclouds.compute.predicates.ImagePredicates;
+import org.jclouds.ec2.EC2ApiMetadata;
+import org.jclouds.ec2.domain.BlockDevice;
+import org.jclouds.ec2.domain.Image;
+import org.jclouds.ec2.domain.Image.ImageType;
+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.services.AMIClient;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+/**
+ * Tests behavior of {@code AMIClient}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true)
+public class AMIClientLiveTest extends BaseComputeServiceContextLiveTest {
+   private TemplateBuilderSpec ebsTemplate;
+
+   public AMIClientLiveTest() {
+      provider = "ec2";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      String ebsSpec = setIfTestSystemPropertyPresent(overrides, provider + ".ebs-template");
+      if (ebsSpec != null)
+         ebsTemplate = TemplateBuilderSpec.parse(ebsSpec);
+      return overrides;
+   }
+
+   protected AMIClient client;
+
+   protected Set<String> imagesToDeregister = Sets.newHashSet();
+   protected Set<String> snapshotsToDelete = Sets.newHashSet();
+   protected String regionId;
+   protected String ebsBackedImageId;
+   protected String ebsBackedImageName = "jcloudstest1";
+
+   @Override
+   @BeforeClass(groups = { "integration", "live" })
+   public void setupContext() {
+      super.setupContext();
+      client = view.unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi().getAMIServices();
+      if (ebsTemplate != null) {
+         Template template = view.getComputeService().templateBuilder().from(ebsTemplate).build();
+         regionId = template.getLocation().getId();
+         for (Image image : client.describeImagesInRegion(regionId)) {
+            if (ebsBackedImageName.equals(image.getName()))
+               client.deregisterImageInRegion(regionId, image.getId());
+         }
+      }
+   }
+
+   public void testDescribeImageNotExists() {
+      assertEquals(client.describeImagesInRegion(null, imageIds("ami-cdf819a3")).size(), 0);
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testDescribeImageBadId() {
+      client.describeImagesInRegion(null, imageIds("asdaasdsa"));
+   }
+
+   public void testDescribeImages() {
+      for (String region : view.unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi().getAvailabilityZoneAndRegionServices()
+            .describeRegions().keySet()) {
+         Set<? extends Image> allResults = client.describeImagesInRegion(region);
+         assertNotNull(allResults);
+         assert allResults.size() >= 2 : allResults.size();
+         Iterator<? extends Image> iterator = allResults.iterator();
+         String id1 = iterator.next().getId();
+         String id2 = iterator.next().getId();
+         Set<? extends Image> twoResults = client.describeImagesInRegion(region, imageIds(id1, id2));
+         assertNotNull(twoResults);
+         assertEquals(twoResults.size(), 2);
+         iterator = twoResults.iterator();
+         assertEquals(iterator.next().getId(), id1);
+         assertEquals(iterator.next().getId(), id2);
+      }
+   }
+
+   @Test
+   public void testCreateAndListEBSBackedImage() throws Exception {
+      ComputeService computeService = view.getComputeService();
+      Snapshot snapshot = createSnapshot(computeService);
+
+      // List of images before...
+      int sizeBefore = computeService.listImages().size();
+
+      // Register a new image...
+      ebsBackedImageId = client.registerUnixImageBackedByEbsInRegion(regionId, ebsBackedImageName, snapshot.getId(),
+            addNewBlockDevice("/dev/sda2", "myvirtual", 1).withDescription("adrian"));
+      imagesToDeregister.add(ebsBackedImageId);
+      final Image ebsBackedImage = Iterables.getOnlyElement(client.describeImagesInRegion(regionId,
+            imageIds(ebsBackedImageId)));
+      assertEquals(ebsBackedImage.getName(), ebsBackedImageName);
+      assertEquals(ebsBackedImage.getImageType(), ImageType.MACHINE);
+      assertEquals(ebsBackedImage.getRootDeviceType(), RootDeviceType.EBS);
+      assertEquals(ebsBackedImage.getRootDeviceName(), "/dev/sda1");
+      assertEquals(ebsBackedImage.getDescription(), "adrian");
+      assertEquals(
+            ebsBackedImage.getEbsBlockDevices().entrySet(),
+            ImmutableMap.of("/dev/sda1", new Image.EbsBlockDevice(snapshot.getId(), snapshot.getVolumeSize(), true),
+                  "/dev/sda2", new Image.EbsBlockDevice(null, 1, false)).entrySet());
+
+      // This is the suggested method to ensure the new image ID is inserted
+      // into the cache
+      // (suggested by adriancole_ on #jclouds)
+      computeService.templateBuilder().imageId(ebsBackedImage.getRegion() + "/" + ebsBackedImageId).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(ebsBackedImage.getRegion() + "/" + ebsBackedImageId));
+      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 template = computeService.templateBuilder().from(ebsTemplate).build();
+      regionId = template.getLocation().getId();
+      Set<? extends NodeMetadata> nodes = computeService.createNodesInGroup("jcloudstest", 1, template);
+      try {
+         String instanceId = Iterables.getOnlyElement(nodes).getProviderId();
+         Reservation<? extends RunningInstance> reservation = Iterables.getOnlyElement(view
+               .unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi().getInstanceServices()
+               .describeInstancesInRegion(regionId, instanceId));
+         RunningInstance instance = Iterables.getOnlyElement(reservation);
+         BlockDevice device = instance.getEbsBlockDevices().get("/dev/sda1");
+         Snapshot snapshot = view.unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi().getElasticBlockStoreServices()
+               .createSnapshotInRegion(regionId, device.getVolumeId());
+         snapshotsToDelete.add(snapshot.getId());
+         return snapshot;
+      } finally {
+         computeService.destroyNodesMatching(Predicates.in(nodes));
+      }
+   }
+
+   @Test(dependsOnMethods = "testCreateAndListEBSBackedImage")
+   public void testGetLaunchPermissionForImage() {
+      System.out.println(client.getLaunchPermissionForImageInRegion(regionId, ebsBackedImageId));
+   }
+
+   @Override
+   @AfterClass(groups = { "integration", "live" })
+   protected void tearDownContext() {
+      for (String imageId : imagesToDeregister)
+         client.deregisterImageInRegion(regionId, imageId);
+      for (String snapshotId : snapshotsToDelete)
+         view.unwrap(EC2ApiMetadata.CONTEXT_TOKEN).getApi().getElasticBlockStoreServices()
+               .deleteSnapshotInRegion(regionId, snapshotId);
+      super.tearDownContext();
+   }
+
+}
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/BaseEC2AsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/BaseEC2AsyncClientTest.java
index 2890eae..39e689d 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/BaseEC2AsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/BaseEC2AsyncClientTest.java
@@ -79,7 +79,7 @@
       }
       
       @Override
-      protected String provideTimeStamp(DateService dateService, int expiration) {
+      protected String provideTimeStamp(DateService dateService) {
          return "2009-11-08T15:54:08.897Z";
       }
 
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/EC2ElasticBlockStoreClientExpectTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/EC2ElasticBlockStoreClientExpectTest.java
new file mode 100644
index 0000000..c032187
--- /dev/null
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/EC2ElasticBlockStoreClientExpectTest.java
@@ -0,0 +1,71 @@
+/**
+ * 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.services;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.ec2.EC2Client;
+import org.jclouds.ec2.domain.Volume;
+import org.jclouds.ec2.internal.BaseEC2ExpectTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "EC2ElasticBlockStoreClientExpectTest")
+public class EC2ElasticBlockStoreClientExpectTest extends BaseEC2ExpectTest<EC2Client> {
+
+   public void testCreateVolumeInAvailabilityZone() {
+      Builder<HttpRequest, HttpResponse> builder = ImmutableMap.<HttpRequest, HttpResponse>builder();
+      builder.put(describeRegionsRequest, describeRegionsResponse);
+      builder.putAll(describeAvailabilityZonesRequestResponse);
+      builder.put(
+            HttpRequest.builder()
+                       .method("POST")
+                       .endpoint(URI.create("https://ec2.us-east-1.amazonaws.com/"))
+                       .headers(ImmutableMultimap.of("Host", "ec2.us-east-1.amazonaws.com"))
+                       .payload(payloadFromStringWithContentType("Action=CreateVolume&AvailabilityZone=us-east-1a&Signature=FB5hTZHKSAuiygoafIdJh1EnfTu0ogC2VfRQOar85mg%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Size=4&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2010-06-15&AWSAccessKeyId=identity", "application/x-www-form-urlencoded")).build(),
+            HttpResponse.builder()
+                        .statusCode(200)
+                        .payload(payloadFromResource("/created_volume.xml")).build());
+      
+      ElasticBlockStoreClient client = requestsSendResponses(builder.build()).getElasticBlockStoreServices();
+      Volume expected =  Volume
+            .builder()
+            .id("vol-2a21e543")
+            .status(Volume.Status.CREATING)
+            .availabilityZone("us-east-1a")
+            .region("us-east-1")
+            .id("vol-2a21e543")
+            .size(1)
+            .createTime(dateService.iso8601DateParse("2009-12-28T05:42:53.000Z"))
+            .build();
+
+      assertEquals(client.createVolumeInAvailabilityZone("us-east-1a", 4), expected);
+   }
+
+}
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceAsyncClientTest.java
index ac0b44c..3ec4a00 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/InstanceAsyncClientTest.java
@@ -99,7 +99,7 @@
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
       assertSaxResponseParserClassEquals(method, InstanceStateChangeHandler.class);
-      assertExceptionParserClassEquals(method, null);
+      assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
 
       checkFilters(request);
    }
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java
index f52baaf..16d597b 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/RunInstancesResponseHandlerTest.java
@@ -29,6 +29,7 @@
 import org.jclouds.ec2.domain.InstanceState;
 import org.jclouds.ec2.domain.InstanceType;
 import org.jclouds.ec2.domain.Reservation;
+import org.jclouds.ec2.domain.RootDeviceType;
 import org.jclouds.ec2.domain.RunningInstance;
 import org.jclouds.http.functions.ParseSax;
 import org.jclouds.rest.internal.GeneratedHttpRequest;
@@ -57,7 +58,7 @@
       assert dateService != null;
    }
 
-   public void testApplyInputStream() {
+   public void testEC2() {
 
       InputStream is = getClass().getResourceAsStream("/run_instances.xml");
 
@@ -90,6 +91,26 @@
       assertEquals(result.toString(), expected.toString());
    }
 
+   public void testCloudBridge() {
+
+      InputStream is = getClass().getResourceAsStream("/run_instances_cloudbridge.xml");
+
+      Reservation<? extends RunningInstance> expected = new Reservation<RunningInstance>(defaultRegion, ImmutableSet
+               .of("default"), ImmutableSet.of(
+
+      new RunningInstance.Builder().region(defaultRegion).groupId("jclouds#greenqloud-computeblock").amiLaunchIndex("0")
+               .imageId("qmi-9ac92558").instanceId("i-01b0dac3").instanceState(InstanceState.PENDING).rawState(
+                        "pending").instanceType(InstanceType.M1_SMALL).keyName("jclouds#greenqloud-computeblock#35")
+                        .launchTime(dateService.iso8601DateParse("2012-06-15T19:06:35.000+00:00"))
+                        .rootDeviceType(RootDeviceType.EBS).availabilityZone("is-1a").build())
+      , "56eeacd9-c790-45c3-85f3-e4380b55e1d8<", null, "r-f847a6ca");
+
+      RunInstancesResponseHandler handler = injector.getInstance(RunInstancesResponseHandler.class);
+      addDefaultRegionToHandler(handler);
+      Reservation<? extends RunningInstance> result = factory.create(handler).parse(is);
+      assertEquals(result.toString(), expected.toString());
+   }
+
    private void addDefaultRegionToHandler(ParseSax.HandlerWithResult<?> handler) {
       GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
       expect(request.getArgs()).andReturn(ImmutableList.<Object> of()).atLeastOnce();
diff --git a/apis/ec2/src/test/resources/amzn_images.xml b/apis/ec2/src/test/resources/amzn_images.xml
index 82f97dc..0b1a767 100644
--- a/apis/ec2/src/test/resources/amzn_images.xml
+++ b/apis/ec2/src/test/resources/amzn_images.xml
@@ -82,5 +82,32 @@
             <blockDeviceMapping/>
             <virtualizationType>paravirtual</virtualizationType>
         </item>
+        <item>
+            <imageId>ami-85457ff1</imageId>
+            <imageLocation>amazon/NameGivesNoClue_Server-2008-R2_SP1-Language_Packs-64Bit-Base-2012.05.10</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>801119661308</imageOwnerId>
+            <isPublic>true</isPublic>
+            <architecture>x86_64</architecture>
+            <imageType>machine</imageType>
+            <platform>windows</platform>
+            <imageOwnerAlias>amazon</imageOwnerAlias>
+            <name>NameGivesNoClue_Server-2008-R2_SP1-Language_Packs-64Bit-Base-2012.05.10</name>
+            <description>Microsoft Windows Server 2008 R2 SP1 Datacenter 64-bit Multi-language AMI provided by Amazon</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <blockDeviceMapping>
+                <item>
+                    <deviceName>/dev/sda1</deviceName>
+                    <ebs>
+                        <snapshotId>snap-84a433ef</snapshotId>
+                        <volumeSize>30</volumeSize>
+                        <deleteOnTermination>true</deleteOnTermination>
+                    </ebs>
+                </item>
+            </blockDeviceMapping>
+            <virtualizationType>hvm</virtualizationType>
+            <hypervisor>xen</hypervisor>
+        </item>
     </imagesSet>
 </DescribeImagesResponse>
diff --git a/apis/ec2/src/test/resources/availabilityZones-ap-northeast-1.xml b/apis/ec2/src/test/resources/availabilityZones-ap-northeast-1.xml
new file mode 100644
index 0000000..129daf5
--- /dev/null
+++ b/apis/ec2/src/test/resources/availabilityZones-ap-northeast-1.xml
@@ -0,0 +1,17 @@
+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>24251909-7fb6-4080-8ca3-fbb5a1ddb02d</requestId>
+    <availabilityZoneInfo>
+        <item>
+            <zoneName>ap-northeast-1a</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>ap-northeast-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>ap-northeast-1b</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>ap-northeast-1</regionName>
+            <messageSet/>
+        </item>
+    </availabilityZoneInfo>
+</DescribeAvailabilityZonesResponse>
\ No newline at end of file
diff --git a/apis/ec2/src/test/resources/availabilityZones-ap-southeast-1.xml b/apis/ec2/src/test/resources/availabilityZones-ap-southeast-1.xml
new file mode 100644
index 0000000..47b33ec
--- /dev/null
+++ b/apis/ec2/src/test/resources/availabilityZones-ap-southeast-1.xml
@@ -0,0 +1,17 @@
+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>22c5332d-c331-4310-b695-e21e76c3fdb0</requestId>
+    <availabilityZoneInfo>
+        <item>
+            <zoneName>ap-southeast-1a</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>ap-southeast-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>ap-southeast-1b</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>ap-southeast-1</regionName>
+            <messageSet/>
+        </item>
+    </availabilityZoneInfo>
+</DescribeAvailabilityZonesResponse>
\ No newline at end of file
diff --git a/apis/ec2/src/test/resources/availabilityZones-eu-west-1.xml b/apis/ec2/src/test/resources/availabilityZones-eu-west-1.xml
new file mode 100644
index 0000000..921216c
--- /dev/null
+++ b/apis/ec2/src/test/resources/availabilityZones-eu-west-1.xml
@@ -0,0 +1,23 @@
+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>386c8215-6ec8-4495-aaa6-8c4175688093</requestId>
+    <availabilityZoneInfo>
+        <item>
+            <zoneName>eu-west-1a</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>eu-west-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>eu-west-1b</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>eu-west-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>eu-west-1c</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>eu-west-1</regionName>
+            <messageSet/>
+        </item>
+    </availabilityZoneInfo>
+</DescribeAvailabilityZonesResponse>
\ No newline at end of file
diff --git a/apis/ec2/src/test/resources/availabilityZones-sa-east-1.xml b/apis/ec2/src/test/resources/availabilityZones-sa-east-1.xml
new file mode 100644
index 0000000..d6f97c6
--- /dev/null
+++ b/apis/ec2/src/test/resources/availabilityZones-sa-east-1.xml
@@ -0,0 +1,17 @@
+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>d2e09bd5-b2ec-4c6b-aac9-6bf1fa217cb5</requestId>
+    <availabilityZoneInfo>
+        <item>
+            <zoneName>sa-east-1a</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>sa-east-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>sa-east-1b</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>sa-east-1</regionName>
+            <messageSet/>
+        </item>
+    </availabilityZoneInfo>
+</DescribeAvailabilityZonesResponse>
\ No newline at end of file
diff --git a/apis/ec2/src/test/resources/availabilityZones-us-east-1.xml b/apis/ec2/src/test/resources/availabilityZones-us-east-1.xml
new file mode 100644
index 0000000..52b521e
--- /dev/null
+++ b/apis/ec2/src/test/resources/availabilityZones-us-east-1.xml
@@ -0,0 +1,35 @@
+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>ed825abf-edbb-4677-83d5-0fbd86ce5a5d</requestId>
+    <availabilityZoneInfo>
+        <item>
+            <zoneName>us-east-1a</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-east-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>us-east-1b</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-east-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>us-east-1c</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-east-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>us-east-1d</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-east-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>us-east-1e</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-east-1</regionName>
+            <messageSet/>
+        </item>
+    </availabilityZoneInfo>
+</DescribeAvailabilityZonesResponse>
\ No newline at end of file
diff --git a/apis/ec2/src/test/resources/availabilityZones-us-west-1.xml b/apis/ec2/src/test/resources/availabilityZones-us-west-1.xml
new file mode 100644
index 0000000..c74ce18
--- /dev/null
+++ b/apis/ec2/src/test/resources/availabilityZones-us-west-1.xml
@@ -0,0 +1,23 @@
+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>dbfb2a5f-ce75-49fb-846b-e2cf2fc5cdbb</requestId>
+    <availabilityZoneInfo>
+        <item>
+            <zoneName>us-west-1a</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-west-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>us-west-1b</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-west-1</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>us-west-1c</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-west-1</regionName>
+            <messageSet/>
+        </item>
+    </availabilityZoneInfo>
+</DescribeAvailabilityZonesResponse>
\ No newline at end of file
diff --git a/apis/ec2/src/test/resources/availabilityZones-us-west-2.xml b/apis/ec2/src/test/resources/availabilityZones-us-west-2.xml
new file mode 100644
index 0000000..a782705
--- /dev/null
+++ b/apis/ec2/src/test/resources/availabilityZones-us-west-2.xml
@@ -0,0 +1,23 @@
+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>e40c2bc4-7231-4df1-a604-202bb8333332</requestId>
+    <availabilityZoneInfo>
+        <item>
+            <zoneName>us-west-2a</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-west-2</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>us-west-2b</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-west-2</regionName>
+            <messageSet/>
+        </item>
+        <item>
+            <zoneName>us-west-2c</zoneName>
+            <zoneState>available</zoneState>
+            <regionName>us-west-2</regionName>
+            <messageSet/>
+        </item>
+    </availabilityZoneInfo>
+</DescribeAvailabilityZonesResponse>
\ No newline at end of file
diff --git a/apis/ec2/src/test/resources/regionEndpoints-all.xml b/apis/ec2/src/test/resources/regionEndpoints-all.xml
new file mode 100644
index 0000000..d577f72
--- /dev/null
+++ b/apis/ec2/src/test/resources/regionEndpoints-all.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DescribeRegionsResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
+    <requestId>8392a998-c7bb-41fc-be15-045f528d7627</requestId>
+    <regionInfo>
+        <item>
+            <regionName>eu-west-1</regionName>
+            <regionEndpoint>ec2.eu-west-1.amazonaws.com</regionEndpoint>
+        </item>
+        <item>
+            <regionName>sa-east-1</regionName>
+            <regionEndpoint>ec2.sa-east-1.amazonaws.com</regionEndpoint>
+        </item>
+        <item>
+            <regionName>us-east-1</regionName>
+            <regionEndpoint>ec2.us-east-1.amazonaws.com</regionEndpoint>
+        </item>
+        <item>
+            <regionName>ap-northeast-1</regionName>
+            <regionEndpoint>ec2.ap-northeast-1.amazonaws.com</regionEndpoint>
+        </item>
+        <item>
+            <regionName>us-west-2</regionName>
+            <regionEndpoint>ec2.us-west-2.amazonaws.com</regionEndpoint>
+        </item>
+        <item>
+            <regionName>us-west-1</regionName>
+            <regionEndpoint>ec2.us-west-1.amazonaws.com</regionEndpoint>
+        </item>
+        <item>
+            <regionName>ap-southeast-1</regionName>
+            <regionEndpoint>ec2.ap-southeast-1.amazonaws.com</regionEndpoint>
+        </item>
+    </regionInfo>
+</DescribeRegionsResponse>
\ No newline at end of file
diff --git a/apis/ec2/src/test/resources/run_instances_cloudbridge.xml b/apis/ec2/src/test/resources/run_instances_cloudbridge.xml
new file mode 100644
index 0000000..37d5ae8
--- /dev/null
+++ b/apis/ec2/src/test/resources/run_instances_cloudbridge.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2010-06-15/">
+  <requestId>56eeacd9-c790-45c3-85f3-e4380b55e1d8</requestId>
+  <reservationId>r-f847a6ca</reservationId>
+  <ownerId>55ed6530-9b32-48f1-acb7-6ec0d3255a65</ownerId>
+  <groupSet>
+    <item>
+      <groupId>jclouds#greenqloud-computeblock</groupId>
+    </item>
+    <item>
+      <groupId>jclouds#greenqloud-computeblock</groupId>
+    </item>
+  </groupSet>
+  <instancesSet>
+    <item>
+      <instanceId>i-01b0dac3</instanceId>
+      <imageId>qmi-9ac92558</imageId>
+      <instanceState>
+        <code>0</code>
+        <name>pending</name>
+      </instanceState>
+      <privateDnsName/>
+      <dnsName/>
+      <reason/>
+      <keyName>jclouds#greenqloud-computeblock#35</keyName>
+      <amiLaunchIndex>0</amiLaunchIndex>
+      <productCodes>
+        <item>
+          <productCode/>
+        </item>
+      </productCodes>
+      <instanceType>m1.small</instanceType>
+      <launchTime>2012-06-15T19:06:35.000+00:00</launchTime>
+      <placement>
+        <availabilityZone>is-1a</availabilityZone>
+      </placement>
+      <kernelId/>
+      <ramdiskId/>
+      <platform/>
+      <monitoring>
+        <state>disabled</state>
+      </monitoring>
+      <subnetId/>
+      <vpcId/>
+      <privateIpAddress/>
+      <groupSet>
+        <item>
+          <groupId>jclouds#greenqloud-computeblock</groupId>
+        </item>
+        <item>
+          <groupId>jclouds#greenqloud-computeblock</groupId>
+        </item>
+      </groupSet>
+      <stateReason>
+        <code>pending</code>
+        <message>pending</message>
+      </stateReason>
+      <architecture/>
+      <rootDeviceType>ebs</rootDeviceType>
+      <rootDeviceName/>
+      <blockDeviceMapping/>
+      <instanceLifecycle/>
+      <spotInstanceRequestId/>
+      <hypervisor>xen</hypervisor>
+    </item>
+  </instancesSet>
+  <requesterId/>
+</RunInstancesResponse>
\ No newline at end of file
diff --git a/apis/elasticstack/pom.xml b/apis/elasticstack/pom.xml
index 7925103..c2e8d7e 100644
--- a/apis/elasticstack/pom.xml
+++ b/apis/elasticstack/pom.xml
@@ -50,13 +50,10 @@
   <properties>
     <test.elasticstack.endpoint>https://api.lon-p.elastichosts.com</test.elasticstack.endpoint>
     <test.elasticstack.api-version>1.0</test.elasticstack.api-version>
-    <test.elasticstack.build-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>38df0986-4d85-4b76-b502-3878ffc80161</test.elasticstack.image-id>
-    <test.elasticstack.image.login-user />
-    <test.elasticstack.image.authenticate-sudo />
-
+    <test.elasticstack.template>imageId=38df0986-4d85-4b76-b502-3878ffc80161</test.elasticstack.template>
     <jclouds.osgi.export>org.jclouds.elasticstack*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -120,9 +117,7 @@
                     <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>
-                    <test.elasticstack.image.login-user>${test.elasticstack.image.login-user}</test.elasticstack.image.login-user>
-                    <test.elasticstack.image.authenticate-sudo>${test.elasticstack.image.authenticate-sudo}</test.elasticstack.image.authenticate-sudo>
+                    <test.elasticstack.template>${test.elasticstack.template}</test.elasticstack.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
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 54c9fd1..1eea179 100644
--- a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackClientLiveTest.java
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackClientLiveTest.java
@@ -55,7 +55,6 @@
 
 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.common.net.HostAndPort;
@@ -83,6 +82,7 @@
    protected RestContext<ElasticStackClient, ElasticStackAsyncClient> cloudStackContext;
    protected Predicate<HostAndPort> socketTester;
    protected Predicate<DriveInfo> driveNotClaimed;
+   protected String imageId;
 
    @BeforeGroups(groups = { "integration", "live" })
    @Override
@@ -96,7 +96,7 @@
       socketTester = new RetryablePredicate<HostAndPort>(new InetSocketAddressConnect(), maxDriveImageTime, 1,
                TimeUnit.SECONDS);
       
-      if (Strings.emptyToNull(imageId) == null) {
+      if (template == null || template.getImageId() == null) {
          imageId = view.getComputeService().templateBuilder().build().getImage().getId();
       }
    }
diff --git a/apis/eucalyptus/pom.xml b/apis/eucalyptus/pom.xml
index 1e71b8d..67848f5 100644
--- a/apis/eucalyptus/pom.xml
+++ b/apis/eucalyptus/pom.xml
@@ -36,13 +36,11 @@
   <properties>
     <test.eucalyptus.endpoint>http://ecc.eucalyptus.com:8773/services/Eucalyptus</test.eucalyptus.endpoint>
     <test.eucalyptus.api-version>2010-06-15</test.eucalyptus.api-version>
-    <test.eucalyptus.build-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 />
-    <test.eucalyptus.image.login-user />
-    <test.eucalyptus.image.authenticate-sudo />
-
+    <test.eucalyptus.template></test.eucalyptus.template>
+    <test.eucalyptus.ebs-template></test.eucalyptus.ebs-template>
     <jclouds.osgi.export>org.jclouds.eucalyptus*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -115,9 +113,8 @@
                     <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>
-                    <test.eucalyptus.image.login-user>${test.eucalyptus.image.login-user}</test.eucalyptus.image.login-user>
-                    <test.eucalyptus.image.authenticate-sudo>${test.eucalyptus.image.authenticate-sudo}</test.eucalyptus.image.authenticate-sudo>
+                    <test.eucalyptus.template>${test.eucalyptus.template}</test.eucalyptus.template>
+                    <test.eucalyptus.ebs-template>${test.eucalyptus.ebs-template}</test.eucalyptus.ebs-template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/apis/eucalyptus/src/main/java/org/jclouds/eucalyptus/EucalyptusApiMetadata.java b/apis/eucalyptus/src/main/java/org/jclouds/eucalyptus/EucalyptusApiMetadata.java
index 80855d3..fd7a798 100644
--- a/apis/eucalyptus/src/main/java/org/jclouds/eucalyptus/EucalyptusApiMetadata.java
+++ b/apis/eucalyptus/src/main/java/org/jclouds/eucalyptus/EucalyptusApiMetadata.java
@@ -17,9 +17,7 @@
  * under the License.
  */
 package org.jclouds.eucalyptus;
-
 import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_PORT_OPEN;
-import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
 import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
 
 import java.util.Properties;
@@ -57,8 +55,8 @@
    
    public static Properties defaultProperties() {
       Properties properties = EC2ApiMetadata.defaultProperties();
-      properties.setProperty(PROPERTY_REGIONS, "Eucalyptus");
-      properties.setProperty(PROPERTY_EC2_AMI_OWNERS, "admin");
+      // in version 3, lowecase 'e' version 2, uppercase 'E'
+      properties.setProperty(PROPERTY_REGIONS, "eucalyptus");
       properties.setProperty(TIMEOUT_PORT_OPEN, 5 * 60 * 1000 + "");
       return properties;
    }
@@ -67,7 +65,7 @@
       protected Builder(){
          super(EC2Client.class, EC2AsyncClient.class);
          id("eucalyptus")
-         .defaultEndpoint("http://173.205.188.130:8773/services/Eucalyptus")
+         .defaultEndpoint("http://partnercloud.eucalyptus.com:8773/services/Eucalyptus/")
          .name("Eucalyptus (EC2 clone) API")
          .defaultProperties(EucalyptusApiMetadata.defaultProperties());
       }
diff --git a/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/config/EucalyptusRestClientModuleExpectTest.java b/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/config/EucalyptusRestClientModuleExpectTest.java
new file mode 100644
index 0000000..722d4b2
--- /dev/null
+++ b/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/config/EucalyptusRestClientModuleExpectTest.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.eucalyptus.config;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.jclouds.eucalyptus.internal.BaseEucalyptusExpectTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.location.Region;
+import org.jclouds.location.Zone;
+import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
+import org.jclouds.location.functions.ZoneToEndpoint;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "EucalyptusRestClientModuleExpectTest")
+public class EucalyptusRestClientModuleExpectTest extends BaseEucalyptusExpectTest<Injector> {
+   private Injector injector;
+
+   public EucalyptusRestClientModuleExpectTest() {
+      Builder<HttpRequest, HttpResponse> builder = ImmutableMap.<HttpRequest, HttpResponse> builder();
+      builder.put(describeRegionsRequest, describeRegionsResponse);
+      builder.put(describeAZRequest, describeAZResponse);
+
+      injector = requestsSendResponses(builder.build());
+   }
+
+   public void testLocationIdAndURIBindings() {
+
+      assertEquals(injector.getInstance(Key.get(new TypeLiteral<Supplier<Set<String>>>() {
+      }, Region.class)).get(), ImmutableSet.<String> of("eucalyptus"));
+
+      assertEquals(injector.getInstance(Key.get(new TypeLiteral<Supplier<Set<String>>>() {
+      }, Zone.class)).get(), ImmutableSet.<String> of("partner01"));
+
+      Map<String, Supplier<URI>> regionToURISupplier = injector.getInstance(
+            Key.get(new TypeLiteral<Supplier<Map<String, Supplier<URI>>>>() {
+            }, Region.class)).get();
+
+      assertEquals(regionToURISupplier.get("eucalyptus").get(), URI.create("http://eucalyptus.partner.eucalyptus.com:8773/services/Eucalyptus"));
+
+      Map<String, Supplier<Set<String>>> regionToZoneIdSupplier = injector.getInstance(
+            Key.get(new TypeLiteral<Supplier<Map<String, Supplier<Set<String>>>>>() {
+            }, Zone.class)).get();
+
+      assertEquals(regionToZoneIdSupplier.get("eucalyptus").get(), ImmutableSet.of("partner01"));
+
+      Map<String, Supplier<URI>> zoneToURISupplier = injector.getInstance(
+            Key.get(new TypeLiteral<Supplier<Map<String, Supplier<URI>>>>() {
+            }, Zone.class)).get();
+
+      assertEquals(zoneToURISupplier.get("partner01").get(), URI.create("http://eucalyptus.partner.eucalyptus.com:8773/services/Eucalyptus"));
+
+   }
+
+   public void testZoneToEndpoint() {
+      assertEquals(injector.getInstance(ZoneToEndpoint.class).apply("partner01"),
+            URI.create("http://eucalyptus.partner.eucalyptus.com:8773/services/Eucalyptus"));
+
+   }
+   
+   public void testRegionToEndpointOrProviderIfNull() {
+      assertEquals(injector.getInstance(RegionToEndpointOrProviderIfNull.class).apply("eucalyptus"),
+            URI.create("http://eucalyptus.partner.eucalyptus.com:8773/services/Eucalyptus"));
+   }
+   
+   @Override
+   public Injector createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
+      return createInjector(fn, module, props);
+   }
+
+}
diff --git a/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/internal/BaseEucalyptusExpectTest.java b/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/internal/BaseEucalyptusExpectTest.java
new file mode 100644
index 0000000..0549fe7
--- /dev/null
+++ b/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/internal/BaseEucalyptusExpectTest.java
@@ -0,0 +1,91 @@
+/**
+ * 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.eucalyptus.internal;
+
+import java.net.URI;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.date.DateService;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.ec2.EC2AsyncClient;
+import org.jclouds.ec2.EC2Client;
+import org.jclouds.ec2.config.EC2RestClientModule;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.internal.BaseRestClientExpectTest;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.google.inject.Module;
+import com.google.inject.Provides;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public abstract class BaseEucalyptusExpectTest<T> extends BaseRestClientExpectTest<T> {
+   protected static final String CONSTANT_DATE = "2012-04-16T15:54:08.897Z";
+   
+   protected DateService dateService = new SimpleDateFormatDateService();
+   
+   protected HttpRequest describeRegionsRequest = HttpRequest
+         .builder()
+         .method("POST")
+         .endpoint(URI.create("http://partnercloud.eucalyptus.com:8773/services/Eucalyptus/"))
+         .headers(ImmutableMultimap.of("Host", "partnercloud.eucalyptus.com:8773"))
+         .payload(payloadFromStringWithContentType(
+                  "Action=DescribeRegions&Signature=tp9WpT8503JdxIXYu6Eu2Dmu%2Bd%2FpqviST7N7Fvr%2FyQo%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2010-06-15&AWSAccessKeyId=identity",
+                  MediaType.APPLICATION_FORM_URLENCODED)).build();
+   
+   protected HttpResponse describeRegionsResponse = HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResourceWithContentType("/regionEndpoints-euca.xml", MediaType.APPLICATION_XML))
+         .build();
+   
+   protected HttpRequest describeAZRequest = HttpRequest.builder()
+         .method("POST")
+         .endpoint(URI.create("http://eucalyptus.partner.eucalyptus.com:8773/services/Eucalyptus/"))
+         .headers(ImmutableMultimap.of("Host", "eucalyptus.partner.eucalyptus.com:8773"))
+         .payload(payloadFromStringWithContentType(
+                "Action=DescribeAvailabilityZones&Signature=i4OkMed1sqQV7hlF%2Fl1KdbQwmwJ4Fh4o9W32eVGayPk%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-04-16T15%3A54%3A08.897Z&Version=2010-06-15&AWSAccessKeyId=identity",
+                MediaType.APPLICATION_FORM_URLENCODED)).build();
+   
+   protected HttpResponse describeAZResponse = HttpResponse.builder().statusCode(200)
+          .payload(payloadFromResourceWithContentType(
+                "/availabilityZones-eucalyptus.xml", MediaType.APPLICATION_XML)).build();               
+
+
+   public BaseEucalyptusExpectTest() {
+      provider = "eucalyptus";
+   }
+
+   @ConfiguresRestClient
+   private static final class TestEucalyptusRestClientModule extends EC2RestClientModule<EC2Client, EC2AsyncClient> {
+      @Override
+      @Provides
+      protected String provideTimeStamp(DateService dateService) {
+         return CONSTANT_DATE;
+      }
+   }
+
+   @Override
+   protected Module createModule() {
+      return new TestEucalyptusRestClientModule();
+   }
+}
diff --git a/apis/eucalyptus/src/test/resources/availabilityZones-eucalyptus.xml b/apis/eucalyptus/src/test/resources/availabilityZones-eucalyptus.xml
new file mode 100644
index 0000000..0c5a052
--- /dev/null
+++ b/apis/eucalyptus/src/test/resources/availabilityZones-eucalyptus.xml
@@ -0,0 +1,11 @@
+<DescribeAvailabilityZonesResponse
+	xmlns="http://ec2.amazonaws.com/doc/2010-06-15/">
+	<availabilityZoneInfo>
+		<item>
+			<zoneName>partner01</zoneName>
+			<zoneState>173.205.188.7 arn:euca:eucalyptus:partner01:cluster:cc_01/</zoneState>
+			<regionName />
+			<messageSet />
+		</item>
+	</availabilityZoneInfo>
+</DescribeAvailabilityZonesResponse>
\ No newline at end of file
diff --git a/apis/eucalyptus/src/test/resources/regionEndpoints-euca.xml b/apis/eucalyptus/src/test/resources/regionEndpoints-euca.xml
new file mode 100644
index 0000000..a3b205c
--- /dev/null
+++ b/apis/eucalyptus/src/test/resources/regionEndpoints-euca.xml
@@ -0,0 +1,12 @@
+<DescribeRegionsResponse xmlns="http://ec2.amazonaws.com/doc/2010-06-15/">
+	<regionInfo>
+		<item>
+			<regionName>eucalyptus</regionName>
+			<regionEndpoint>http://eucalyptus.partner.eucalyptus.com:8773/services/Eucalyptus</regionEndpoint>
+		</item>
+		<item>
+			<regionName>walrus</regionName>
+			<regionEndpoint>http://walrus.partner.eucalyptus.com:8773/services/Walrus</regionEndpoint>
+		</item>
+	</regionInfo>
+</DescribeRegionsResponse>
\ No newline at end of file
diff --git a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java
index 36ea828..c8d3b0d 100644
--- a/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java
+++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemAsyncBlobStoreTest.java
@@ -858,7 +858,7 @@
 
         // nothing expected
         if (null == expectedBlobKeys || 0 == expectedBlobKeys.size()) {
-            assertTrue(blobsRetrieved.isEmpty(), "Wrong blob number retrieved in the containter [" + containerName + "]");
+            assertTrue(blobsRetrieved.isEmpty(), "Wrong blob number retrieved in the container [" + containerName + "]");
             return;
         }
 
@@ -868,7 +868,7 @@
             expectedBlobKeysCopy.add(value);
         }
         assertEquals(blobsRetrieved.size(), expectedBlobKeysCopy.size(),
-                "Wrong blob number retrieved in the containter [" + containerName + "]");
+                "Wrong blob number retrieved in the container [" + containerName + "]");
         for (StorageMetadata data : blobsRetrieved) {
             String blobName = data.getName();
             if (!expectedBlobKeysCopy.remove(blobName)) {
diff --git a/apis/nova/pom.xml b/apis/nova/pom.xml
index d58260f..5d6ac6c 100644
--- a/apis/nova/pom.xml
+++ b/apis/nova/pom.xml
@@ -36,14 +36,12 @@
   <properties>
     <test.nova.endpoint>http://localhost:8773/services/Cloud</test.nova.endpoint>
     <test.nova.api-version>1.1</test.nova.api-version>
-    <test.nova.build-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 />
-    <test.nova.image.login-user />
-    <test.nova.image.authenticate-sudo />
-    <test.ssh.keyfile.public />
-    <test.ssh.keyfile.private />
+    <test.nova.template></test.nova.template>
+    <test.ssh.keyfile.public></test.ssh.keyfile.public>
+    <test.ssh.keyfile.private></test.ssh.keyfile.private>
 
     <jclouds.osgi.export>org.jclouds.openstack.nova*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
@@ -122,9 +120,7 @@
                     <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>
-                    <test.nova.image.login-user>${test.nova.image.login-user}</test.nova.image.login-user>
-                    <test.nova.image.authenticate-sudo>${test.nova.image.authenticate-sudo}</test.nova.image.authenticate-sudo>
+                    <test.nova.template>${test.nova.template}</test.nova.template>
                     <test.ssh.keyfile.public>${test.ssh.keyfile.public}</test.ssh.keyfile.public>
                     <test.ssh.keyfile.private>${test.ssh.keyfile.private}</test.ssh.keyfile.private>
                   </systemPropertyVariables>
diff --git a/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaClientLiveTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaClientLiveTest.java
index 769a3af..397ee7e 100644
--- a/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaClientLiveTest.java
+++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaClientLiveTest.java
@@ -214,7 +214,7 @@
       while (server == null) {
          String serverName = serverPrefix + "createserver" + new SecureRandom().nextInt();
          try {
-            server = client.createServer(serverName, imageId, flavorId, withFile("/etc/jclouds.txt",
+            server = client.createServer(serverName, template.getImageId(), flavorId, withFile("/etc/jclouds.txt",
                      "nova".getBytes()).withMetadata(metadata));
          } catch (UndeclaredThrowableException e) {
             HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
diff --git a/apis/openstack-keystone/pom.xml b/apis/openstack-keystone/pom.xml
index 4004151..3107a62 100644
--- a/apis/openstack-keystone/pom.xml
+++ b/apis/openstack-keystone/pom.xml
@@ -43,9 +43,7 @@
     <test.openstack-keystone.credential>FIXME_CREDENTIALS</test.openstack-keystone.credential>
     <test.jclouds.keystone.credential-type>passwordCredentials</test.jclouds.keystone.credential-type>
 
-    <jclouds.osgi.export>org.jclouds.openstack.v2_0*;version="${project.version}",
-      org.jclouds.openstack.keystone.v2_0*;version="${project.version}" 
-    </jclouds.osgi.export>
+    <jclouds.osgi.export>org.jclouds.openstack*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.rest.internal;version="${project.version}",
       org.jclouds*;version="${project.version}",
diff --git a/apis/openstack-nova-ec2/pom.xml b/apis/openstack-nova-ec2/pom.xml
index 63ee14f..94fba78 100644
--- a/apis/openstack-nova-ec2/pom.xml
+++ b/apis/openstack-nova-ec2/pom.xml
@@ -36,13 +36,11 @@
   <properties>
     <test.openstack-nova-ec2.endpoint>http://localhost:8773/services/Cloud</test.openstack-nova-ec2.endpoint>
     <test.openstack-nova-ec2.api-version>2010-06-15</test.openstack-nova-ec2.api-version>
-    <test.openstack-nova-ec2.build-version />
+    <test.openstack-nova-ec2.build-version></test.openstack-nova-ec2.build-version>
     <test.openstack-nova-ec2.identity>FIXME_IDENTITY</test.openstack-nova-ec2.identity>
     <test.openstack-nova-ec2.credential>FIXME_CREDENTIAL</test.openstack-nova-ec2.credential>
-    <test.openstack-nova-ec2.image-id />
-    <test.openstack-nova-ec2.image.login-user />
-    <test.openstack-nova-ec2.image.authenticate-sudo />
-
+    <test.openstack-nova-ec2.template></test.openstack-nova-ec2.template>
+    <test.openstack-nova-ec2.ebs-template></test.openstack-nova-ec2.ebs-template>
     <jclouds.osgi.export>org.jclouds.openstack.nova.ec2*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -120,9 +118,8 @@
                     <test.openstack-nova-ec2.build-version>${test.openstack-nova-ec2.build-version}</test.openstack-nova-ec2.build-version>
                     <test.openstack-nova-ec2.identity>${test.openstack-nova-ec2.identity}</test.openstack-nova-ec2.identity>
                     <test.openstack-nova-ec2.credential>${test.openstack-nova-ec2.credential}</test.openstack-nova-ec2.credential>
-                    <test.openstack-nova-ec2.image-id>${test.openstack-nova-ec2.image-id}</test.openstack-nova-ec2.image-id>
-                    <test.openstack-nova-ec2.image.login-user>${test.openstack-nova-ec2.image.login-user}</test.openstack-nova-ec2.image.login-user>
-                    <test.openstack-nova-ec2.image.authenticate-sudo>${test.openstack-nova-ec2.image.authenticate-sudo}</test.openstack-nova-ec2.image.authenticate-sudo>
+                    <test.openstack-nova-ec2.template>${test.openstack-nova-ec2.template}</test.openstack-nova-ec2.template>
+                    <test.openstack-nova-ec2.ebs-template>${test.openstack-nova-ec2.ebs-template}</test.openstack-nova-ec2.ebs-template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java
index 69b7eed..d1730d9 100644
--- a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java
+++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/internal/BaseNovaEC2RestClientExpectTest.java
@@ -2,10 +2,8 @@
 
 import java.net.URI;
 
-import javax.inject.Named;
 import javax.ws.rs.core.MediaType;
 
-import org.jclouds.Constants;
 import org.jclouds.date.DateService;
 import org.jclouds.date.internal.SimpleDateFormatDateService;
 import org.jclouds.http.HttpRequest;
@@ -45,8 +43,7 @@
    private static final class TestNovaEC2RestClientModule extends NovaEC2RestClientModule {
       @Override
       @Provides
-      protected String provideTimeStamp(final DateService dateService,
-               @Named(Constants.PROPERTY_SESSION_INTERVAL) final int expiration) {
+      protected String provideTimeStamp(DateService dateService) {
          return CONSTANT_DATE;
       }
    }
diff --git a/apis/openstack-nova/pom.xml b/apis/openstack-nova/pom.xml
index 445f625..44935e3 100644
--- a/apis/openstack-nova/pom.xml
+++ b/apis/openstack-nova/pom.xml
@@ -38,12 +38,10 @@
     <test.openstack-nova.endpoint>http://localhost:5000</test.openstack-nova.endpoint>
     <!-- keystone version -->
     <test.openstack-nova.api-version>1.1</test.openstack-nova.api-version>
-    <test.openstack-nova.build-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.login-user />
-    <test.openstack-nova.image.authenticate-sudo />
+    <test.openstack-nova.template></test.openstack-nova.template>
     <test.jclouds.openstack-nova.auto-allocate-floating-ips>false</test.jclouds.openstack-nova.auto-allocate-floating-ips>
     <test.jclouds.keystone.credential-type>passwordCredentials</test.jclouds.keystone.credential-type>
 
@@ -130,9 +128,7 @@
                     <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>
-                    <test.openstack-nova.image.login-user>${test.openstack-nova.image.login-user}</test.openstack-nova.image.login-user>
-                    <test.openstack-nova.image.authenticate-sudo>${test.openstack-nova.image.authenticate-sudo}</test.openstack-nova.image.authenticate-sudo>
+                    <test.openstack-nova.template>${test.openstack-nova.template}</test.openstack-nova.template>
                     <test.jclouds.openstack-nova.auto-allocate-floating-ips>${test.jclouds.openstack-nova.auto-allocate-floating-ips}</test.jclouds.openstack-nova.auto-allocate-floating-ips>
                     <test.jclouds.keystone.credential-type>${test.jclouds.keystone.credential-type}</test.jclouds.keystone.credential-type>
                   </systemPropertyVariables>
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ServerInZoneToNodeMetadata.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ServerInZoneToNodeMetadata.java
index f5d1957..548126a 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ServerInZoneToNodeMetadata.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ServerInZoneToNodeMetadata.java
@@ -165,7 +165,7 @@
             }
          });
       } catch (NoSuchElementException e) {
-         logger.debug("could not find %s with id(%s) for server(%s)", type, objectId, serverInZone);
+         logger.trace("could not find %s with id(%s) for server(%s)", type, objectId, serverInZone);
       }
       return null;
    }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java
index fa1ec8f..34ca70d 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java
@@ -103,9 +103,16 @@
 
    @Override
    public ToStringHelper string() {
-      return super.string().add("autoAssignFloatingIp", autoAssignFloatingIp)
-            .add("securityGroupNames", securityGroupNames).add("generateKeyPair", generateKeyPair)
-            .add("keyPairName", keyPairName).add("userData", userData);
+      ToStringHelper toString = super.string();
+      if (!autoAssignFloatingIp)
+         toString.add("autoAssignFloatingIp", autoAssignFloatingIp);
+      if (securityGroupNames.size() != 0)
+         toString.add("securityGroupNames", securityGroupNames);
+      if (generateKeyPair)
+         toString.add("generateKeyPair", generateKeyPair);
+      toString.add("keyPairName", keyPairName);
+      toString.add("userData", userData);
+      return toString;
    }
 
    public static final NovaTemplateOptions NONE = new NovaTemplateOptions();
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java
index ca6ae80..3b1805e 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java
@@ -32,14 +32,12 @@
 
 import javax.inject.Inject;
 import javax.inject.Named;
-import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.jclouds.Constants;
 import org.jclouds.compute.config.CustomizationResponse;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.functions.GroupNamingConvention;
 import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
 import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
@@ -71,7 +69,6 @@
    private final LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache;
    private final LoadingCache<ZoneAndName, KeyPair> keyPairCache;
    private final NovaClient novaClient;
-   private final Provider<TemplateBuilder> templateBuilderProvider;
 
    @Inject
    protected ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet(
@@ -80,13 +77,11 @@
             GroupNamingConvention.Factory namingConvention,
             CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
             @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor,
-            Provider<TemplateBuilder> templateBuilderProvider,
             AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode,
             LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache,
             LoadingCache<ZoneAndName, KeyPair> keyPairCache, NovaClient novaClient) {
       super(addNodeWithTagStrategy, listNodesStrategy, namingConvention, executor,
                customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
-      this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider");
       this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache");
       this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
       this.allocateAndAddFloatingIpToNode = checkNotNull(allocateAndAddFloatingIpToNode,
@@ -98,16 +93,7 @@
    public Map<?, Future<Void>> execute(String group, int count, Template template, Set<NodeMetadata> goodNodes,
             Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
 
-      Template mutableTemplate;
-      // ensure we don't mutate the input template, fromTemplate ignores imageId so
-      // build directly from imageId if we have it
-      if (template.getImage() != null && template.getImage().getId() != null) {
-         mutableTemplate = templateBuilderProvider.get().imageId(template.getImage().getId()).fromTemplate(template)
-                  .build();
-         // otherwise build from generic parameters
-      } else {
-         mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build();
-      }
+      Template mutableTemplate = template.clone();
 
       NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(mutableTemplate.getOptions());
 
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
index 7a63d68..00aa5ee 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
@@ -26,8 +26,8 @@
 
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
+import java.util.Set;
 
 import javax.inject.Inject;
 
@@ -40,8 +40,8 @@
 import org.jclouds.util.Preconditions2;
 
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -133,10 +133,18 @@
    }
 
    protected ToStringHelper string() {
-      return toStringHelper("").add("keyName", keyName).add("securityGroupNames", securityGroupNames)
-              .add("metadata", metadata).add("personality", personality)
-              .add("adminPassPresent", adminPass != null)
-              .add("userData", userData == null ? null : new String(userData));
+      ToStringHelper toString = Objects.toStringHelper("").omitNullValues();
+      toString.add("keyName", keyName);
+      if (securityGroupNames.size() > 0)
+         toString.add("securityGroupNames", securityGroupNames);
+      if (metadata.size() > 0)
+         toString.add("metadata", metadata);
+      if (personality.size() > 0)
+         toString.add("personality", personality);
+      if (adminPass != null)
+         toString.add("adminPassPresent", true);
+      toString.add("userData", userData == null ? null : new String(userData));
+      return toString;
    }
 
    @Override
diff --git a/apis/swift/src/main/java/org/jclouds/openstack/swift/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java b/apis/swift/src/main/java/org/jclouds/openstack/swift/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
index a5b02c5..12481c2 100644
--- a/apis/swift/src/main/java/org/jclouds/openstack/swift/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
+++ b/apis/swift/src/main/java/org/jclouds/openstack/swift/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
@@ -204,7 +204,7 @@
 
                                 String eTag = client.putObjectManifest(container, key);
                                 logger.debug(String.format("multipart upload of %s to container %s" +
-                                        " succeffully finished with %s retries", key, container, errors.get()));
+                                        " successfully finished with %s retries", key, container, errors.get()));
                                 return eTag;
                             } catch (Exception ex) {
                                 RuntimeException rtex = Throwables2.getFirstThrowableOfType(ex, RuntimeException.class);
diff --git a/apis/vcloud/pom.xml b/apis/vcloud/pom.xml
index bdc529b..4f23e0f 100644
--- a/apis/vcloud/pom.xml
+++ b/apis/vcloud/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.vcloud.endpoint>FIXME</test.vcloud.endpoint>
     <test.vcloud.api-version>1.0</test.vcloud.api-version>
-    <test.vcloud.build-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 />
-    <test.vcloud.image.login-user />
-    <test.vcloud.image.authenticate-sudo />
-
+    <test.vcloud.template></test.vcloud.template>
     <jclouds.osgi.export>org.jclouds.vcloud*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -118,9 +115,7 @@
                     <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>
-                    <test.vcloud.image.login-user>${test.vcloud.image.login-user}</test.vcloud.image.login-user>
-                    <test.vcloud.image.authenticate-sudo>${test.vcloud.image.authenticate-sudo}</test.vcloud.image.authenticate-sudo>
+                    <test.vcloud.template>${test.vcloud.template}</test.vcloud.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java
index ec55d60..5ed397c 100644
--- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java
+++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java
@@ -139,7 +139,7 @@
    public void testListRootUsesDelimiter() throws InterruptedException {
       String containerName = getContainerName();
       try {
-         String prefix = "rootdelimeter";
+         String prefix = "rootdelimiter";
          addTenObjectsUnderPrefix(containerName, prefix);
          add15UnderRoot(containerName);
          PageSet<? extends StorageMetadata> container = view.getBlobStore().list(containerName);
diff --git a/common/aws/src/main/java/org/jclouds/aws/config/FormSigningRestClientModule.java b/common/aws/src/main/java/org/jclouds/aws/config/FormSigningRestClientModule.java
index 12b90f3..761a292 100644
--- a/common/aws/src/main/java/org/jclouds/aws/config/FormSigningRestClientModule.java
+++ b/common/aws/src/main/java/org/jclouds/aws/config/FormSigningRestClientModule.java
@@ -21,10 +21,8 @@
 import java.util.Date;
 import java.util.Map;
 
-import javax.inject.Named;
 import javax.inject.Singleton;
 
-import org.jclouds.Constants;
 import org.jclouds.aws.filters.FormSigner;
 import org.jclouds.date.DateService;
 import org.jclouds.date.TimeStamp;
@@ -59,9 +57,8 @@
    
    @Provides
    @TimeStamp
-   protected String provideTimeStamp(final DateService dateService,
-            @Named(Constants.PROPERTY_SESSION_INTERVAL) final int expiration) {
-      return dateService.iso8601DateFormat(new Date(System.currentTimeMillis() + (expiration * 1000)));
+   protected String provideTimeStamp(DateService dateService) {
+      return dateService.iso8601DateFormat(new Date(System.currentTimeMillis()));
    }
 
    @Provides
diff --git a/common/aws/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java b/common/aws/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java
index e4dc779..615801b 100644
--- a/common/aws/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java
+++ b/common/aws/src/main/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContent.java
@@ -112,7 +112,7 @@
                               ".InUse")))
                      || (message != null && (message.indexOf("already exists") != -1 || message.indexOf("is in use") != -1)))
                exception = new IllegalStateException(message, exception);
-            else if ("AuthFailure".equals(errorCode))
+            else if (errorCode != null && errorCode.indexOf("AuthFailure") != -1)
                exception = new AuthorizationException(message, exception);
             else if (message != null
                      && (message.indexOf("Invalid id") != -1 || message.indexOf("Failed to bind") != -1))
diff --git a/common/aws/src/test/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContentTest.java b/common/aws/src/test/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContentTest.java
index 6df2308..85c6f90 100644
--- a/common/aws/src/test/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContentTest.java
+++ b/common/aws/src/test/java/org/jclouds/aws/handlers/ParseAWSErrorFromXmlContentTest.java
@@ -56,7 +56,13 @@
       assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "",
                "<Error><Code>Monster.NotFound</Code></Error>", ResourceNotFoundException.class);
    }
-
+   
+   @Test
+   public void test400WithCloudBridgeNotFoundSetsResourceNotFoundException() {
+      assertCodeMakes("POST", URI.create("https://api.greenqloud.com/"), 400, "",
+               "<Error><Code>Client.InvalidInstanceID.NotFound</Code></Error>", ResourceNotFoundException.class);
+   }
+   
    @Test
    public void test400WithInvalidIdIllegalArgumentException() {
       assertCodeMakes("POST", URI.create("https://ec2.us-east-1.amazonaws.com"), 400, "HTTP/1.1 400", "",
@@ -164,6 +170,12 @@
                "<Error><Code>AuthFailure</Code></Error>", AuthorizationException.class);
    }
 
+   @Test
+   public void test400WithCloudBridgeAuthFailureSetsAuthorizationException() {
+      assertCodeMakes("POST", URI.create("https://api.greenqloud.com/"), 400, "",
+               "<Error><Code>Client.AuthFailure</Code></Error>", AuthorizationException.class);
+   }
+
    private void assertCodeMakes(String method, URI uri, int statusCode, String message, String content,
             Class<? extends Exception> expected) {
       assertCodeMakes(method, uri, statusCode, message, "text/xml", content, expected);
diff --git a/common/openstack/pom.xml b/common/openstack/pom.xml
index af29166..af8aa2a 100644
--- a/common/openstack/pom.xml
+++ b/common/openstack/pom.xml
@@ -35,18 +35,7 @@
     <packaging>bundle</packaging>
 
     <properties>
-        <jclouds.osgi.export>org.jclouds.openstack.config*;version="${project.version}",
-          org.jclouds.openstack.domain*;version="${project.version}"
-          org.jclouds.openstack.filters*;version="${project.version}"
-          org.jclouds.openstack.functions*;version="${project.version}"
-          org.jclouds.openstack.handlers*;version="${project.version}"
-          org.jclouds.openstack.internal*;version="${project.version}"
-          org.jclouds.openstack.keystone.v1_1*;version="${project.version}"
-          org.jclouds.openstack.options*;version="${project.version}"
-          org.jclouds.openstack.predicates*;version="${project.version}"
-          org.jclouds.openstack.reference*;version="${project.version}"
-          org.jclouds.openstack.services*;version="${project.version}"
-        </jclouds.osgi.export>
+        <jclouds.osgi.export>org.jclouds.openstack*;version="${project.version}";-noimport:=true</jclouds.osgi.export>
         <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
      </properties>
 
diff --git a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java
index a3e263a..e2ba40c 100644
--- a/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java
+++ b/compute/src/main/java/org/jclouds/compute/callables/RunScriptOnNodeAsInitScriptUsingSsh.java
@@ -119,7 +119,7 @@
             ssh.connect();
             ssh.exec("rm " + initFile);
             ssh.exec(Statements.appendFile(initFile, Splitter.on('\n').split(init.render(OsFamily.UNIX)),
-                  AppendFile.DELIMETER + "_" + init.getInstanceName()).render(OsFamily.UNIX));
+                  AppendFile.DELIMITER + "_" + init.getInstanceName()).render(OsFamily.UNIX));
          }
 
          ssh.exec("chmod 755 " + initFile);
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 e94df32..24c3fd0 100644
--- a/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java
+++ b/compute/src/main/java/org/jclouds/compute/callables/SudoAwareInitManager.java
@@ -90,7 +90,7 @@
       String command = (runAsRoot && Predicates.in(ImmutableSet.of("start", "stop", "run")).apply(action)) ? execScriptAsRoot(action)
             : execScriptAsDefaultUser(action);
       returnVal = runCommand(command);
-      if ("status".equals(action))
+      if (ImmutableSet.of("status", "stdout", "stderr").contains(action))
          logger.trace("<< %s(%d)", action, returnVal.getExitStatus());
       else if (computeLogger.isTraceEnabled())
          computeLogger.trace("<< %s[%s]", action, returnVal);
@@ -103,8 +103,8 @@
       String statement = String.format("[%s] as %s@%s", command.replace(
             node.getCredentials().getPassword() != null ? node.getCredentials().getPassword() : "XXXXX", "XXXXX"), ssh
             .getUsername(), ssh.getHostAddress());
-      if (command.endsWith("status"))
-         logger.trace(">> running " + statement);
+      if (command.endsWith("status") || command.endsWith("stdout") || command.endsWith("stderr"))
+         logger.trace(">> running %s", statement);
       else 
          computeLogger.debug(">> running " + statement);
       ExecResponse returnVal = ssh.exec(command);
diff --git a/compute/src/main/java/org/jclouds/compute/domain/Template.java b/compute/src/main/java/org/jclouds/compute/domain/Template.java
index 9dd98c5..5b6e873 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/Template.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/Template.java
@@ -47,4 +47,11 @@
     * options for launching this template, like run scripts or inbound ports
     */
    TemplateOptions getOptions();
+   
+   /**
+    * clone this template
+    * 
+    * @see Object#clone
+    */
+   Template clone();
 }
\ No newline at end of file
diff --git a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java
index 1192374..604aa64 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/TemplateBuilderSpec.java
@@ -152,7 +152,7 @@
          .put("authenticateSudo", new AuthenticateSudoParser())
          .put("locationId", new LocationIdParser())
          .build();
-   
+
    @VisibleForTesting
    String hardwareId;
    @VisibleForTesting
@@ -553,4 +553,63 @@
       }
    }
    
+   public String getHardwareId() {
+      return hardwareId;
+   }
+
+   public Double getMinCores() {
+      return minCores;
+   }
+
+   public Integer getMinRam() {
+      return minRam;
+   }
+
+   public String getHypervisorMatches() {
+      return hypervisorMatches;
+   }
+
+   public String getImageId() {
+      return imageId;
+   }
+
+   public String getImageNameMatches() {
+      return imageNameMatches;
+   }
+
+   public OsFamily getOsFamily() {
+      return osFamily;
+   }
+
+   public String getOsVersionMatches() {
+      return osVersionMatches;
+   }
+
+   public Boolean getOs64Bit() {
+      return os64Bit;
+   }
+
+   public String getOsArchMatches() {
+      return osArchMatches;
+   }
+
+   public String getOsDescriptionMatches() {
+      return osDescriptionMatches;
+   }
+
+   public String getLoginUser() {
+      return loginUser;
+   }
+
+   public Boolean getAuthenticateSudo() {
+      return authenticateSudo;
+   }
+
+   public String getLocationId() {
+      return locationId;
+   }
+
+   public String getSpecification() {
+      return specification;
+   }
 }
diff --git a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
index 648b828..e8d4155 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
@@ -24,6 +24,7 @@
 import static com.google.common.collect.Iterables.find;
 import static com.google.common.collect.Iterables.size;
 import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Iterables.tryFind;
 import static com.google.common.collect.Lists.newArrayList;
 import static java.lang.String.format;
 import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
@@ -57,6 +58,8 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
@@ -403,27 +406,23 @@
 
    private Predicate<Hardware> buildHardwarePredicate() {
       List<Predicate<Hardware>> predicates = newArrayList();
-      if (hardwareId != null) {
-         predicates.add(hardwareIdPredicate);
-      } else {
-         if (location != null)
-            predicates.add(new Predicate<Hardware>() {
+      if (location != null)
+         predicates.add(new Predicate<Hardware>() {
 
-               @Override
-               public boolean apply(Hardware input) {
-                  return locationPredicate.apply(input);
-               }
+            @Override
+            public boolean apply(Hardware input) {
+               return locationPredicate.apply(input);
+            }
 
-               @Override
-               public String toString() {
-                  return locationPredicate.toString();
-               }
-            });
-         if (hypervisor != null)
-            predicates.add(hypervisorPredicate);
-         predicates.add(hardwareCoresPredicate);
-         predicates.add(hardwareRamPredicate);
-      }
+            @Override
+            public String toString() {
+               return locationPredicate.toString();
+            }
+         });
+      if (hypervisor != null)
+         predicates.add(hypervisorPredicate);
+      predicates.add(hardwareCoresPredicate);
+      predicates.add(hardwareRamPredicate);
 
       // looks verbose, but explicit <Hardware> type needed for this to compile
       // properly
@@ -612,35 +611,87 @@
          options = optionsProvider.get();
       logger.debug(">> searching params(%s)", this);
       Set<? extends Image> images = getImages();
+      Set<? extends Hardware> hardwaresToSearch = hardwares.get();
+
+      Image image = null;
+      if (imageId != null) {
+         image = findImageWithId(images);
+         if (currentLocationWiderThan(image.getLocation()))
+            this.location = image.getLocation();
+      }
+      
+      Hardware hardware = null;
+      if (hardwareId != null) {
+         hardware = findHardwareWithId(hardwaresToSearch);
+         if (currentLocationWiderThan(hardware.getLocation()))
+            this.location = hardware.getLocation();
+      }
+      
+      // if the user hasn't specified a location id, or an image or hardware
+      // with location, let's search scoped to the implicit one
       if (location == null)
          location = defaultLocation.get();
       
-      Predicate<Image> imagePredicate = buildImagePredicate();
-      Iterable<? extends Image> supportedImages = filter(images, imagePredicate);
-      if (size(supportedImages) == 0) {
-         if (imagePredicate == idPredicate) {
-            throwNoSuchElementExceptionAfterLoggingImageIds(format("%s not found", idPredicate), images);
-         } else {
-            throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched predicate: %s", imagePredicate),
-                     images);
-         }
+      if (image == null) {
+         Iterable<? extends Image> supportedImages = findSupportedImages(images);
+         if (hardware == null)
+            hardware = resolveHardware(hardwaresToSearch, supportedImages);
+         image = resolveImage(hardware, supportedImages);
+      } else {
+         if (hardware == null)
+            hardware = resolveHardware(hardwaresToSearch, ImmutableSet.of(image));
       }
 
-      Hardware hardware = resolveHardware(hardwareSorter(), supportedImages);
-      Image image = resolveImage(hardware, supportedImages);
-      logger.debug("<<   matched image(%s)", image.getId());
+      logger.debug("<<   matched image(%s) hardware(%s) location(%s)", image.getId(), hardware.getId(),
+            location.getId());
       return new TemplateImpl(image, hardware, location, options);
    }
 
-   protected void throwNoSuchElementExceptionAfterLoggingImageIds(String message, Iterable<? extends Image> images) {
+   private Iterable<? extends Image> findSupportedImages(Set<? extends Image> images) {
+      Predicate<Image> imagePredicate = buildImagePredicate();
+      Iterable<? extends Image> supportedImages = filter(images, imagePredicate);
+      if (size(supportedImages) == 0) {
+         throw throwNoSuchElementExceptionAfterLoggingImageIds(
+               format("no image matched predicate: %s", imagePredicate), images);
+      }
+      return supportedImages;
+   }
+
+   private Image findImageWithId(Set<? extends Image> images) {
+      Image image;
+      // TODO: switch to GetImageStrategy in version 1.5
+      image = tryFind(images, idPredicate).orNull();
+      if (image == null)
+         throwNoSuchElementExceptionAfterLoggingImageIds(format("%s not found", idPredicate), images);
+      return image;
+   }
+
+   private Hardware findHardwareWithId(Set<? extends Hardware> hardwaresToSearch) {
+      Hardware hardware;
+      // TODO: switch to GetHardwareStrategy in version 1.5
+      hardware = tryFind(hardwaresToSearch, hardwareIdPredicate).orNull();
+      if (hardware == null)
+         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(format("%s not found", hardwareIdPredicate),
+               hardwaresToSearch);
+      return hardware;
+   }
+
+   protected NoSuchElementException throwNoSuchElementExceptionAfterLoggingImageIds(String message, Iterable<? extends Image> images) {
       NoSuchElementException exception = new NoSuchElementException(message);
       if (logger.isTraceEnabled())
          logger.warn(exception, "image ids that didn't match: %s", transform(images, imageToId));
       throw exception;
    }
 
-   protected Hardware resolveHardware(Ordering<Hardware> hardwareOrdering, final Iterable<? extends Image> images) {
-      Set<? extends Hardware> hardwarel = hardwares.get();
+   protected NoSuchElementException throwNoSuchElementExceptionAfterLoggingHardwareIds(String message, Iterable<? extends Hardware> hardwares) {
+      NoSuchElementException exception = new NoSuchElementException(message);
+      if (logger.isTraceEnabled())
+         logger.warn(exception, "hardware ids that didn't match: %s", transform(hardwares, hardwareToId));
+      throw exception;
+   }
+   
+   protected Hardware resolveHardware(Set<? extends Hardware> hardwarel, final Iterable<? extends Image> images) {
+      Ordering<Hardware> hardwareOrdering = hardwareSorter();
       Iterable<? extends Hardware> hardwaresThatAreCompatibleWithOurImages = ImmutableSet.of();
       try {
          hardwaresThatAreCompatibleWithOurImages = filter(hardwarel, new Predicate<Hardware>() {
@@ -667,11 +718,7 @@
       }
       if (size(hardwaresThatAreCompatibleWithOurImages) == 0) {
          String message = format("no hardware profiles support images matching params: %s", toString());
-         NoSuchElementException exception = new NoSuchElementException(message);
-         if (logger.isTraceEnabled())
-            logger.warn(exception, "hardware profiles %s\nimage ids %s", transform(hardwarel, hardwareToId), transform(
-                     images, imageToId));
-         throw exception;
+         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(message, hardwaresThatAreCompatibleWithOurImages);
       }
       Predicate<Hardware> hardwarePredicate = buildHardwarePredicate();
       Hardware hardware;
@@ -679,13 +726,9 @@
          hardware = hardwareOrdering.max(filter(hardwaresThatAreCompatibleWithOurImages, hardwarePredicate));
       } catch (NoSuchElementException exception) {
          String message = format("no hardware profiles match params: %s", hardwarePredicate);
-         exception = new NoSuchElementException(message);
-         if (logger.isTraceEnabled())
-            logger.warn(exception, "hardware profiles %s", transform(hardwaresThatAreCompatibleWithOurImages,
-                     hardwareToId));
-         throw exception;
+         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(message, hardwaresThatAreCompatibleWithOurImages);
       }
-      logger.debug("<<   matched hardware(%s)", hardware.getId());
+      logger.trace("<<   matched hardware(%s)", hardware.getId());
       return hardware;
    }
 
@@ -741,59 +784,55 @@
 
    private Predicate<Image> buildImagePredicate() {
       List<Predicate<Image>> predicates = newArrayList();
-      if (imageId != null) {
-         predicates.add(idPredicate);
-      } else {
-         if (location != null)
-            predicates.add(new Predicate<Image>() {
+      if (location != null)
+         predicates.add(new Predicate<Image>() {
 
-               @Override
-               public boolean apply(Image input) {
-                  return locationPredicate.apply(input);
-               }
+            @Override
+            public boolean apply(Image input) {
+               return locationPredicate.apply(input);
+            }
 
-               @Override
-               public String toString() {
-                  return locationPredicate.toString();
-               }
-            });
+            @Override
+            public String toString() {
+               return locationPredicate.toString();
+            }
+         });
 
-         final List<Predicate<OperatingSystem>> osPredicates = newArrayList();
-         if (osFamily != null)
-            osPredicates.add(osFamilyPredicate);
-         if (osName != null)
-            osPredicates.add(osNamePredicate);
-         if (osDescription != null)
-            osPredicates.add(osDescriptionPredicate);
-         if (osVersion != null)
-            osPredicates.add(osVersionPredicate);
-         if (os64Bit != null)
-            osPredicates.add(os64BitPredicate);
-         if (osArch != null)
-            osPredicates.add(osArchPredicate);
-         if (osPredicates.size() > 0)
-            predicates.add(new Predicate<Image>() {
+      final List<Predicate<OperatingSystem>> osPredicates = newArrayList();
+      if (osFamily != null)
+         osPredicates.add(osFamilyPredicate);
+      if (osName != null)
+         osPredicates.add(osNamePredicate);
+      if (osDescription != null)
+         osPredicates.add(osDescriptionPredicate);
+      if (osVersion != null)
+         osPredicates.add(osVersionPredicate);
+      if (os64Bit != null)
+         osPredicates.add(os64BitPredicate);
+      if (osArch != null)
+         osPredicates.add(osArchPredicate);
+      if (osPredicates.size() > 0)
+         predicates.add(new Predicate<Image>() {
 
-               @Override
-               public boolean apply(Image input) {
-                  return and(osPredicates).apply(input.getOperatingSystem());
-               }
+            @Override
+            public boolean apply(Image input) {
+               return and(osPredicates).apply(input.getOperatingSystem());
+            }
 
-               @Override
-               public String toString() {
-                  return and(osPredicates).toString();
-               }
+            @Override
+            public String toString() {
+               return and(osPredicates).toString();
+            }
 
-            });
-         if (imageVersion != null)
-            predicates.add(imageVersionPredicate);
-         if (imageName != null)
-            predicates.add(imageNamePredicate);
-         if (imageDescription != null)
-            predicates.add(imageDescriptionPredicate);
-         if (imagePredicate != null)
-            predicates.add(imagePredicate);
-      }
+         });
+      if (imageVersion != null)
+         predicates.add(imageVersionPredicate);
+      if (imageName != null)
+         predicates.add(imageNamePredicate);
+      if (imageDescription != null)
+         predicates.add(imageDescriptionPredicate);
+      if (imagePredicate != null)
+         predicates.add(imagePredicate);
 
       // looks verbose, but explicit <Image> type needed for this to compile
       // properly
@@ -958,11 +997,40 @@
 
    @Override
    public String toString() {
-      return "[biggest=" + biggest + ", fastest=" + fastest + ", imageName=" + imageName + ", imageDescription="
-            + imageDescription + ", imageId=" + imageId + ", imagePredicate=" + imagePredicate + ", imageVersion=" + imageVersion + ", location=" + location
-            + ", minCores=" + minCores + ", minRam=" + minRam + ", osFamily=" + osFamily + ", osName=" + osName
-            + ", osDescription=" + osDescription + ", osVersion=" + osVersion + ", osArch=" + osArch + ", os64Bit="
-            + os64Bit + ", hardwareId=" + hardwareId + ", hypervisor=" + hypervisor + "]";
+      return string().toString();
+   }
+
+   /**
+    * @since 1.5
+    */
+   protected ToStringHelper string() {
+      ToStringHelper toString = Objects.toStringHelper("").omitNullValues();
+      if (biggest)
+         toString.add("biggest", biggest);
+      if (fastest)
+         toString.add("fastest", fastest);
+      toString.add("imageName", imageName);
+      toString.add("imageDescription", imageDescription);
+      toString.add("imageId", imageId);
+      toString.add("imagePredicate", imagePredicate);
+      toString.add("imageVersion", imageVersion);
+      if (location != null)
+         toString.add("locationId", location.getId());
+      if (minCores >0) //TODO: make non-primitive
+         toString.add("minCores", minCores);
+      if (minRam >0) //TODO: make non-primitive
+         toString.add("minRam", minRam);
+      if (minRam >0) //TODO: make non-primitive
+         toString.add("minRam", minRam);
+      toString.add("osFamily", osFamily);
+      toString.add("osName", osName);
+      toString.add("osDescription", osDescription);
+      toString.add("osVersion", osVersion);
+      toString.add("osArch", osArch);
+      toString.add("os64Bit", os64Bit);
+      toString.add("hardwareId", hardwareId);
+      toString.add("hypervisor", hypervisor);
+      return toString;
    }
 
    @Override
diff --git a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateImpl.java b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateImpl.java
index ee5c096..bc0eb83 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateImpl.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateImpl.java
@@ -129,8 +129,8 @@
     * {@inheritDoc}
     */
    @Override
-   protected Object clone() throws CloneNotSupportedException {
-      return new TemplateImpl(image, size, location, options);
+   public Template clone() {
+      return new TemplateImpl(image, size, location, options.clone());
    }
 
 }
diff --git a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
index 04e676a..92b07ac 100644
--- a/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
+++ b/compute/src/main/java/org/jclouds/compute/internal/BaseComputeService.java
@@ -338,9 +338,9 @@
     */
    @Override
    public Set<ComputeMetadata> listNodes() {
-      logger.debug(">> listing nodes");
+      logger.trace(">> listing nodes");
       Set<ComputeMetadata> set = newLinkedHashSet(listNodesStrategy.listNodes());
-      logger.debug("<< list(%d)", set.size());
+      logger.trace("<< list(%d)", set.size());
       return set;
    }
 
@@ -350,9 +350,9 @@
    @Override
    public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter) {
       checkNotNull(filter, "filter");
-      logger.debug(">> listing node details matching(%s)", filter);
+      logger.trace(">> listing node details matching(%s)", filter);
       Set<NodeMetadata> set = newLinkedHashSet(listNodesStrategy.listDetailsOnNodesMatching(filter));
-      logger.debug("<< list(%d)", set.size());
+      logger.trace("<< list(%d)", set.size());
       return set;
    }
 
diff --git a/compute/src/main/java/org/jclouds/compute/options/RunScriptOptions.java b/compute/src/main/java/org/jclouds/compute/options/RunScriptOptions.java
index 534b8b7..a0ad298 100644
--- a/compute/src/main/java/org/jclouds/compute/options/RunScriptOptions.java
+++ b/compute/src/main/java/org/jclouds/compute/options/RunScriptOptions.java
@@ -455,11 +455,23 @@
    }
 
    protected ToStringHelper string() {
-      return Objects.toStringHelper("").add("loginUser", loginUser)
-            .add("loginPasswordPresent", (loginPassword != null))
-            .add("loginPrivateKeyPresent", (loginPrivateKey != null)).add("authenticateSudo", authenticateSudo)
-            .add("port:seconds", port + ":" + seconds).add("taskName", taskName).add("runAsRoot", runAsRoot)
-            .add("blockOnComplete", blockOnComplete).add("wrapInInitScript", wrapInInitScript);
+      ToStringHelper toString = Objects.toStringHelper("").omitNullValues();
+      toString.add("loginUser", loginUser);
+      if (loginPassword != null)
+         toString.add("loginPasswordPresent", true);
+      if (loginPrivateKey != null)
+         toString.add("loginPrivateKeyPresent", true);
+      toString.add("authenticateSudo", authenticateSudo);
+      if (port != -1 && seconds != -1) // TODO: not primitives
+         toString.add("blockOnPort:seconds", port + ":" + seconds);
+      toString.add("taskName", taskName);
+      if (!runAsRoot)
+         toString.add("runAsRoot", runAsRoot);
+      if (!blockOnComplete)
+         toString.add("blockOnComplete", blockOnComplete);
+      if (!wrapInInitScript)
+         toString.add("wrapInInitScript", wrapInInitScript);
+      return toString;
    }
 
 }
diff --git a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java
index e9066f8..ce41ef3 100644
--- a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java
+++ b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java
@@ -317,7 +317,9 @@
 
    }
 
-   protected Set<Integer> inboundPorts = ImmutableSet.of(22);
+   private final static Set<Integer> DEFAULT_INBOUND_PORTS = ImmutableSet.of(22);
+   
+   protected Set<Integer> inboundPorts = DEFAULT_INBOUND_PORTS;
 
    protected Statement script;
 
@@ -352,11 +354,24 @@
 
    @Override
    public ToStringHelper string() {
-      return super.string().add("inboundPorts", inboundPorts).add("scriptPresent", script != null)
-            .add("publicKeyPresent", publicKey != null).add("privateKeyPresent", privateKey != null).add("tags", tags)
-            .add("blockUntilRunning", blockUntilRunning).add("tags", tags).add("userMetadata", userMetadata);
+      ToStringHelper toString = super.string();
+      if (!DEFAULT_INBOUND_PORTS.equals(inboundPorts))
+         toString.add("inboundPorts", inboundPorts);
+      if (script != null)
+         toString.add("scriptPresent", true);
+      if (publicKey != null)
+         toString.add("publicKeyPresent", true);
+      if (privateKey != null)
+         toString.add("privateKeyPresent", true);
+      if (!blockUntilRunning)
+         toString.add("blockUntilRunning", blockUntilRunning);
+      if (tags.size() != 0)
+         toString.add("tags", tags);
+      if (userMetadata.size() != 0)
+         toString.add("userMetadata", userMetadata);
+      return toString;
    }
-   
+
    public int[] getInboundPorts() {
       return Ints.toArray(inboundPorts);
    }
diff --git a/compute/src/main/java/org/jclouds/compute/RunScriptData.java b/compute/src/test/java/org/jclouds/compute/RunScriptData.java
similarity index 98%
rename from compute/src/main/java/org/jclouds/compute/RunScriptData.java
rename to compute/src/test/java/org/jclouds/compute/RunScriptData.java
index c2ecded..3087d11 100644
--- a/compute/src/main/java/org/jclouds/compute/RunScriptData.java
+++ b/compute/src/test/java/org/jclouds/compute/RunScriptData.java
@@ -75,7 +75,7 @@
       return InitScript.builder()
                .name("jboss")
                .home(JBOSS_HOME)
-               .exportVariables(ImmutableMap.of("jbossHome", JBOSS_HOME))
+               .exportVariables(ImmutableMap.of("JBOSS_HOME", JBOSS_HOME))
                .init(appendFile(JBOSS_HOME + "/standalone/configuration/standalone-custom.xml", Splitter.on('\n').split(configuration)))
                .run(interpret(new StringBuilder().append("java ").append(' ')
                                  .append("-server -Xms128m -Xmx128m -XX:MaxPermSize=128m -Djava.net.preferIPv4Stack=true -XX:+UseFastAccessorMethods -XX:+TieredCompilation -Xverify:none -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000").append(' ')
diff --git a/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java b/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
index 9a80ef4..1f4bb92 100644
--- a/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
+++ b/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
@@ -63,6 +63,9 @@
    protected Location region = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1").description("us-east-1")
          .parent(provider).build();
    
+   protected Location region2 = new LocationBuilder().scope(LocationScope.REGION).id("us-east-2").description("us-east-2")
+         .parent(provider).build();
+   
    @SuppressWarnings("unchecked")
    public void testLocationPredicateWhenComputeMetadataIsNotLocationBound() {
       Image image = createMock(Image.class);
@@ -371,7 +374,7 @@
          // make sure big data is not in the exception message
          assertEquals(
                   e.getMessage(),
-                  "no hardware profiles support images matching params: [biggest=false, fastest=false, imageName=null, imageDescription=null, imageId=us-east-1/imageId, imagePredicate=null, imageVersion=null, location=[id=us-east-1, scope=REGION, description=us-east-1, parent=aws-ec2, iso3166Codes=[], metadata={}], minCores=0.0, minRam=0, osFamily=null, osName=null, osDescription=null, osVersion=null, osArch=null, os64Bit=false, hardwareId=null, hypervisor=null]");
+                  "no hardware profiles support images matching params: {imageId=us-east-1/imageId, locationId=us-east-1, os64Bit=false}");
          verify(image);
          verify(os);
          verify(defaultTemplate);
@@ -849,9 +852,63 @@
       TemplateBuilder templateBuilder = templateBuilderProvider.get().minRam(512).osFamily(OsFamily.UBUNTU)
             .hypervisorMatches("OpenVZ").osVersionMatches("1[10].[10][04]").os64Bit(true);
 
+      assertEquals(templateBuilder.toString(), "{minRam=512, minRam=512, osFamily=ubuntu, osVersion=1[10].[10][04], os64Bit=true, hypervisor=OpenVZ}");
+
       Template template = templateBuilder.build();
       assertEquals(template.getHardware().getHypervisor(), "OpenVZ");
       assertEquals(template.getImage().getId(), "Ubuntu 11.04 64-bit");
 
    }
+   
+
+   @Test
+   public void testImageLocationNonDefault() {
+
+      final Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .<Location> of(region));
+      final Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet
+            .<Image> of(
+                  new ImageBuilder()
+                        .id("us-east-2/ami-ffff")
+                        .providerId("ami-ffff")
+                        .name("Ubuntu 11.04 x64")
+                        .description("Ubuntu 11.04 x64")
+                        .location(region2)
+                        .status(Status.AVAILABLE)
+                        .operatingSystem(
+                              OperatingSystem.builder().name("Ubuntu 11.04 x64").description("Ubuntu 11.04 x64")
+                                    .is64Bit(true).version("11.04").family(OsFamily.UBUNTU).build()).build()));
+
+      final Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+            .<Hardware> of(
+                  new HardwareBuilder()
+                        .ids("m1.small").ram(512)
+                        .processors(ImmutableList.of(new Processor(1, 1.0)))
+                        .volumes(ImmutableList.<Volume> of(new VolumeImpl((float) 5, true, true))).build()));
+
+      final Provider<TemplateOptions> optionsProvider = new Provider<TemplateOptions>() {
+
+         @Override
+         public TemplateOptions get() {
+            return new TemplateOptions();
+         }
+
+      };
+      Provider<TemplateBuilder> templateBuilderProvider = new Provider<TemplateBuilder>() {
+
+         @Override
+         public TemplateBuilder get() {
+            return createTemplateBuilder(null, locations, images, hardwares, region, optionsProvider, this);
+         }
+
+      };
+
+      TemplateBuilder templateBuilder = templateBuilderProvider.get().hardwareId("m1.small").imageId("us-east-2/ami-ffff");
+
+      assertEquals(templateBuilder.toString(), "{imageId=us-east-2/ami-ffff, hardwareId=m1.small}");
+
+      Template template = templateBuilder.build();
+      assertEquals(template.getLocation().getId(), "us-east-2");
+
+   }
 }
diff --git a/compute/src/test/java/org/jclouds/compute/internal/BaseGenericComputeServiceContextLiveTest.java b/compute/src/test/java/org/jclouds/compute/internal/BaseGenericComputeServiceContextLiveTest.java
index c26b11f..bc7a2eb 100644
--- a/compute/src/test/java/org/jclouds/compute/internal/BaseGenericComputeServiceContextLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/internal/BaseGenericComputeServiceContextLiveTest.java
@@ -24,6 +24,7 @@
 
 import org.jclouds.apis.BaseViewLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.domain.TemplateBuilderSpec;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.domain.LoginCredentials.Builder;
 import org.jclouds.io.CopyInputStreamInputSupplierMap;
@@ -41,9 +42,7 @@
  */
 public abstract class BaseGenericComputeServiceContextLiveTest<W extends ComputeServiceContext> extends BaseViewLiveTest<W> {
 
-   protected String imageId;
-   protected String loginUser;
-   protected String authenticateSudo;
+   protected TemplateBuilderSpec template;
    protected LoginCredentials loginCredentials = LoginCredentials.builder().user("root").build();
 
    // isolate tests from eachother, as default credentialStore is static
@@ -53,19 +52,19 @@
    @Override
    protected Properties setupProperties() {
       Properties overrides = super.setupProperties();
-      imageId = setIfTestSystemPropertyPresent(overrides, provider + ".image-id");
-      loginUser = setIfTestSystemPropertyPresent(overrides, provider + ".image.login-user");
-      authenticateSudo = setIfTestSystemPropertyPresent(overrides, provider + ".image.authenticate-sudo");
-
-      if (loginUser != null) {
-         Iterable<String> userPass = Splitter.on(':').split(loginUser);
-         Builder loginCredentialsBuilder = LoginCredentials.builder();
-         loginCredentialsBuilder.user(Iterables.get(userPass, 0));
-         if (Iterables.size(userPass) == 2)
-            loginCredentialsBuilder.password(Iterables.get(userPass, 1));
-         if (authenticateSudo != null)
-            loginCredentialsBuilder.authenticateSudo(Boolean.valueOf(authenticateSudo));
-         loginCredentials = loginCredentialsBuilder.build();
+      String spec = setIfTestSystemPropertyPresent(overrides, provider + ".template");
+      if (spec != null) {
+         template = TemplateBuilderSpec.parse(spec);
+         if (template.getLoginUser() != null) {
+            Iterable<String> userPass = Splitter.on(':').split(template.getLoginUser());
+            Builder loginCredentialsBuilder = LoginCredentials.builder();
+            loginCredentialsBuilder.user(Iterables.get(userPass, 0));
+            if (Iterables.size(userPass) == 2)
+               loginCredentialsBuilder.password(Iterables.get(userPass, 1));
+            if (template.getAuthenticateSudo() != null)
+               loginCredentialsBuilder.authenticateSudo(template.getAuthenticateSudo());
+            loginCredentials = loginCredentialsBuilder.build();
+         }
       }
       return overrides;
    }
diff --git a/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java b/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
index d79e9e5..cb9505e 100644
--- a/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
@@ -261,9 +261,9 @@
       ComputeServiceContext context = null;
       try {
          Properties overrides = setupProperties();
-         String login = loginUser != null ? loginUser : "foo:bar";
+         String login = template != null && template.getLoginUser() != null ? template.getLoginUser() : "foo:bar";
          overrides.setProperty(propertyKey + ".image.login-user", login);
-         boolean auth = authenticateSudo != null ? Boolean.valueOf(authenticateSudo) : true;
+         boolean auth = template != null && template.getAuthenticateSudo() != null ? template.getAuthenticateSudo() : true;
          overrides.setProperty(propertyKey + ".image.authenticate-sudo", auth + "");
 
          context = createView(overrides, ImmutableSet.<Module>of(credentialStoreModule));
diff --git a/compute/src/test/java/org/jclouds/compute/options/TemplateOptionsTest.java b/compute/src/test/java/org/jclouds/compute/options/TemplateOptionsTest.java
index 1081d82..486ad27 100644
--- a/compute/src/test/java/org/jclouds/compute/options/TemplateOptionsTest.java
+++ b/compute/src/test/java/org/jclouds/compute/options/TemplateOptionsTest.java
@@ -45,6 +45,7 @@
    public void testinstallPrivateKey() throws IOException {
       TemplateOptions options = new TemplateOptions();
       options.installPrivateKey("-----BEGIN RSA PRIVATE KEY-----");
+      assertEquals(options.toString(), "{privateKeyPresent=true}");
       assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----");
    }
 
@@ -75,6 +76,7 @@
    public void testauthorizePublicKey() throws IOException {
       TemplateOptions options = new TemplateOptions();
       options.authorizePublicKey("ssh-rsa");
+      assertEquals(options.toString(), "{publicKeyPresent=true}");
       assertEquals(options.getPublicKey(), "ssh-rsa");
    }
 
@@ -105,6 +107,7 @@
    public void testblockOnPort() {
       TemplateOptions options = new TemplateOptions();
       options.blockOnPort(22, 30);
+      assertEquals(options.toString(), "{blockOnPort:seconds=22:30}");
       assertEquals(options.getPort(), 22);
       assertEquals(options.getSeconds(), 30);
 
@@ -148,6 +151,7 @@
    @Test
    public void testinboundPortsStatic() {
       TemplateOptions options = inboundPorts(22, 30);
+      assertEquals(options.toString(), "{inboundPorts=[22, 30]}");
       assertEquals(options.getInboundPorts()[0], 22);
       assertEquals(options.getInboundPorts()[1], 30);
    }
@@ -155,6 +159,7 @@
    @Test
    public void testblockUntilRunningDefault() {
       TemplateOptions options = new TemplateOptions();
+      assertEquals(options.toString(), "{}");
       assertEquals(options.shouldBlockUntilRunning(), true);
    }
 
@@ -162,6 +167,7 @@
    public void testblockUntilRunning() {
       TemplateOptions options = new TemplateOptions();
       options.blockUntilRunning(false);
+      assertEquals(options.toString(), "{blockUntilRunning=false}");
       assertEquals(options.shouldBlockUntilRunning(), false);
    }
 
diff --git a/core/src/main/java/org/jclouds/JcloudsVersion.java b/core/src/main/java/org/jclouds/JcloudsVersion.java
index be7e72b..eaef9fd 100644
--- a/core/src/main/java/org/jclouds/JcloudsVersion.java
+++ b/core/src/main/java/org/jclouds/JcloudsVersion.java
@@ -33,6 +33,7 @@
 
 /**
  * @author Andrew Phillips
+ * @author Adrian Cole
  */
 public class JcloudsVersion {
     @VisibleForTesting
@@ -105,10 +106,10 @@
 
         String alphaOrBetaOrReleaseCandidateVersionIfPresent = versionMatcher.group(4);
         if (alphaOrBetaOrReleaseCandidateVersionIfPresent != null) {
-            Integer alphaOrReleaseCandidateVersion = Integer.valueOf(versionMatcher.group(5));
+            Integer alphaOrBetaOrReleaseCandidateVersion = Integer.valueOf(versionMatcher.group(5));
             if (alphaOrBetaOrReleaseCandidateVersionIfPresent.equals(ALPHA_VERSION_IDENTIFIER)) {
                 alpha = true;
-                alphaVersion = alphaOrReleaseCandidateVersion;
+                alphaVersion = alphaOrBetaOrReleaseCandidateVersion;
                 beta = false;
                 betaVersion = null;
                 releaseCandidate = false;
@@ -117,7 +118,7 @@
                 alpha = false;
                 alphaVersion = null;
                 beta = true;
-                betaVersion = alphaOrReleaseCandidateVersion;
+                betaVersion = alphaOrBetaOrReleaseCandidateVersion;
                 releaseCandidate = false;
                 releaseCandidateVersion = null;
             } else {
@@ -126,7 +127,7 @@
                 beta = false;
                 betaVersion = null;
                 releaseCandidate = true;
-                releaseCandidateVersion = alphaOrReleaseCandidateVersion;
+                releaseCandidateVersion = alphaOrBetaOrReleaseCandidateVersion;
             }
         } else {
             alpha = false;
diff --git a/core/src/main/java/org/jclouds/apis/ApiRegistry.java b/core/src/main/java/org/jclouds/apis/ApiRegistry.java
new file mode 100644
index 0000000..78071db
--- /dev/null
+++ b/core/src/main/java/org/jclouds/apis/ApiRegistry.java
@@ -0,0 +1,46 @@
+/**
+ * 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.apis;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A registry for holding {@link org.jclouds.apis.ApiMetadata}.
+ */
+public class ApiRegistry {
+
+  private static final Set<ApiMetadata> apis = new HashSet<ApiMetadata>();
+
+  public static void registerApi(ApiMetadata api) {
+    apis.add(api);
+  }
+
+  public static void unRegisterApi(ApiMetadata api) {
+    apis.remove(api);
+  }
+
+  public static Iterable<ApiMetadata> fromRegistry() {
+    return Iterable.class.cast(apis);
+  }
+
+  public static void clear() {
+    apis.clear();
+  }
+}
diff --git a/core/src/main/java/org/jclouds/apis/Apis.java b/core/src/main/java/org/jclouds/apis/Apis.java
index fbb4c14..484a6c5 100644
--- a/core/src/main/java/org/jclouds/apis/Apis.java
+++ b/core/src/main/java/org/jclouds/apis/Apis.java
@@ -69,7 +69,7 @@
     * @return all available apis
     */
    public static Iterable<ApiMetadata> all() {
-      return fromServiceLoader();
+      return Iterables.concat(fromServiceLoader(), ApiRegistry.fromRegistry());
    }
 
    /**
diff --git a/core/src/main/java/org/jclouds/crypto/SshKeys.java b/core/src/main/java/org/jclouds/crypto/SshKeys.java
index e085d7a..1813851 100644
--- a/core/src/main/java/org/jclouds/crypto/SshKeys.java
+++ b/core/src/main/java/org/jclouds/crypto/SshKeys.java
@@ -124,13 +124,14 @@
 
    /**
     * 
-    * @param used
+    * @param generator
     *           to generate RSA key pairs
+    * @param rand
+    *           for initializing {@code generator}
     * @return new 2048 bit keyPair
     * @see Crypto#rsaKeyPairGenerator()
     */
-   public static KeyPair generateRsaKeyPair(KeyPairGenerator generator) {
-      SecureRandom rand = new SecureRandom();
+   public static KeyPair generateRsaKeyPair(KeyPairGenerator generator, SecureRandom rand) {
       generator.initialize(2048, rand);
       return generator.genKeyPair();
    }
@@ -140,15 +141,15 @@
     */
    public static Map<String, String> generate() {
       try {
-         return generate(KeyPairGenerator.getInstance("RSA"));
+         return generate(KeyPairGenerator.getInstance("RSA"), new SecureRandom());
       } catch (NoSuchAlgorithmException e) {
          propagate(e);
          return null;
       }
    }
 
-   public static Map<String, String> generate(KeyPairGenerator generator) {
-      KeyPair pair = generateRsaKeyPair(generator);
+   public static Map<String, String> generate(KeyPairGenerator generator, SecureRandom rand) {
+      KeyPair pair = generateRsaKeyPair(generator, rand);
       Builder<String, String> builder = ImmutableMap.builder();
       builder.put("public", encodeAsOpenSSH(RSAPublicKey.class.cast(pair.getPublic())));
       builder.put("private", encodeAsPem(RSAPrivateKey.class.cast(pair.getPrivate())));
diff --git a/core/src/main/java/org/jclouds/events/handlers/DeadEventLoggingHandler.java b/core/src/main/java/org/jclouds/events/handlers/DeadEventLoggingHandler.java
index 20f709c..c4fbafa 100644
--- a/core/src/main/java/org/jclouds/events/handlers/DeadEventLoggingHandler.java
+++ b/core/src/main/java/org/jclouds/events/handlers/DeadEventLoggingHandler.java
@@ -47,6 +47,6 @@
      */
     @Subscribe
     public final void handleDeadEvent(DeadEvent deadEvent) {
-        logger.warn("detected dead event %s", deadEvent.getEvent());
+        logger.trace("detected dead event %s", deadEvent.getEvent());
     }
 }
diff --git a/core/src/main/java/org/jclouds/io/payloads/BasePayload.java b/core/src/main/java/org/jclouds/io/payloads/BasePayload.java
index 020a0da..53f523c 100644
--- a/core/src/main/java/org/jclouds/io/payloads/BasePayload.java
+++ b/core/src/main/java/org/jclouds/io/payloads/BasePayload.java
@@ -60,7 +60,7 @@
     */
    @Override
    public void writeTo(OutputStream outstream) throws IOException {
-      checkState(!written || isRepeatable(), "can only be writted to an outputstream once");
+      checkState(!written || isRepeatable(), "can only write to an outputStream once");
       written = true;
       InputStream in = getInput();
       try {
diff --git a/core/src/main/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNull.java b/core/src/main/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNull.java
index 52942bb..1548240 100644
--- a/core/src/main/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNull.java
+++ b/core/src/main/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNull.java
@@ -56,10 +56,15 @@
 
    @Override
    public URI apply(@Nullable Object from) {
-      if (from == null || from.equals(defaultProvider))
+      if (from == null)
          return defaultUri.get();
       checkArgument(from instanceof String, "region is a String argument");
       Map<String, Supplier<URI>> regionToEndpoint = regionToEndpointSupplier.get();
+      if (from.equals(defaultProvider)){
+         if (regionToEndpoint.containsKey(from))
+            return regionToEndpoint.get(from).get();
+         return defaultUri.get();
+      }
       checkArgument(regionToEndpoint.containsKey(from),
             "requested location %s, which is not in the configured locations: %s", from, regionToEndpoint);
       return regionToEndpoint.get(from).get();
diff --git a/core/src/main/java/org/jclouds/location/suppliers/derived/ZoneIdToURIFromJoinOnRegionIdToURI.java b/core/src/main/java/org/jclouds/location/suppliers/derived/ZoneIdToURIFromJoinOnRegionIdToURI.java
new file mode 100644
index 0000000..7bbde01
--- /dev/null
+++ b/core/src/main/java/org/jclouds/location/suppliers/derived/ZoneIdToURIFromJoinOnRegionIdToURI.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.location.suppliers.derived;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.location.Region;
+import org.jclouds.location.Zone;
+import org.jclouds.location.suppliers.ZoneIdToURISupplier;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+
+/**
+ * 
+ */
+@Singleton
+public class ZoneIdToURIFromJoinOnRegionIdToURI implements ZoneIdToURISupplier {
+
+   private final Supplier<Map<String, Supplier<URI>>> regionIdToURISupplier;
+   private final Supplier<Map<String, Supplier<Set<String>>>> regionIdToZoneIdsSupplier;
+
+   @Inject
+   protected ZoneIdToURIFromJoinOnRegionIdToURI(@Region Supplier<Map<String, Supplier<URI>>> regionIdToURISupplier,
+         @Zone Supplier<Map<String, Supplier<Set<String>>>> regionIdToZoneIdsSupplier) {
+      this.regionIdToURISupplier = regionIdToURISupplier;
+      this.regionIdToZoneIdsSupplier = regionIdToZoneIdsSupplier;
+   }
+
+   @Override
+   public Map<String, Supplier<URI>> get() {
+      Builder<String, Supplier<URI>> builder = ImmutableMap.<String, Supplier<URI>> builder();
+      for (Entry<String, Supplier<URI>> regionToURI : regionIdToURISupplier.get().entrySet()) {
+         for (String zone : regionIdToZoneIdsSupplier.get().get(regionToURI.getKey()).get()) {
+            builder.put(zone, regionToURI.getValue());
+         }
+      }
+      return builder.build();
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/osgi/Activator.java b/core/src/main/java/org/jclouds/osgi/Activator.java
index 64de199..e6ee8f1 100644
--- a/core/src/main/java/org/jclouds/osgi/Activator.java
+++ b/core/src/main/java/org/jclouds/osgi/Activator.java
@@ -18,13 +18,14 @@
  */
 package org.jclouds.osgi;
 
+import org.jclouds.apis.ApiRegistry;
 import org.jclouds.providers.ProviderRegistry;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 
 public class Activator implements BundleActivator {
 
-  ProviderBundleListener bundleListener = new ProviderBundleListener();
+  MetadataBundleListener bundleListener = new MetadataBundleListener();
 
   /**
    * Called when this bundle is started so the Framework can perform the
@@ -43,6 +44,7 @@
    */
   @Override
   public void start(BundleContext context) throws Exception {
+    bundleListener.start(context);
     context.addBundleListener(bundleListener);
   }
 
@@ -65,7 +67,9 @@
    */
   @Override
   public void stop(BundleContext context) throws Exception {
+    bundleListener.stop(context);
     context.removeBundleListener(bundleListener);
     ProviderRegistry.clear();
+    ApiRegistry.clear();
   }
 }
diff --git a/core/src/main/java/org/jclouds/osgi/MetadataBundleListener.java b/core/src/main/java/org/jclouds/osgi/MetadataBundleListener.java
new file mode 100644
index 0000000..75d9886
--- /dev/null
+++ b/core/src/main/java/org/jclouds/osgi/MetadataBundleListener.java
@@ -0,0 +1,208 @@
+/**
+ * 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.osgi;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiRegistry;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.ProviderRegistry;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link BundleListener} that listens for {@link BundleEvent} and searches for {@link org.jclouds.providers.ProviderMetadata} and {@ling org.jclouds.apis.ApiMetadata} in newly
+ * installed Bundles. This is used as a workaround for OSGi environments where the ServiceLoader cannot cross bundle
+ * boundaries.
+ */
+public class MetadataBundleListener implements BundleListener {
+
+  private Map<Long, ProviderMetadata> providerMetadataMap = new HashMap<Long, ProviderMetadata>();
+  private Map<Long, ApiMetadata> apiMetadataMap = new HashMap<Long, ApiMetadata>();
+
+
+  public void start(BundleContext bundleContext) {
+    bundleContext.addBundleListener(this);
+    for (Bundle bundle : bundleContext.getBundles()) {
+      if (bundle.getState() == Bundle.ACTIVE) {
+        ProviderMetadata providerMetadata = getProviderMetadata(bundle);
+        ApiMetadata apiMetadata = getApiMetadata(bundle);
+
+        if (providerMetadata != null) {
+          ProviderRegistry.registerProvider(providerMetadata);
+          providerMetadataMap.put(bundle.getBundleId(), providerMetadata);
+        }
+        if (apiMetadata != null) {
+          ApiRegistry.registerApi(apiMetadata);
+          apiMetadataMap.put(bundle.getBundleId(), apiMetadata);
+        }
+      }
+    }
+  }
+
+  public void stop(BundleContext bundleContext) {
+    providerMetadataMap.clear();
+    apiMetadataMap.clear();
+  }
+
+  @Override
+  public void bundleChanged(BundleEvent event) {
+    ProviderMetadata providerMetadata;
+    ApiMetadata apiMetadata;
+    switch (event.getType()) {
+      case BundleEvent.STARTED:
+        providerMetadata = getProviderMetadata(event.getBundle());
+        apiMetadata = getApiMetadata(event.getBundle());
+        if (providerMetadata != null) {
+          ProviderRegistry.registerProvider(providerMetadata);
+          providerMetadataMap.put(event.getBundle().getBundleId(), providerMetadata);
+        }
+        if (apiMetadata != null) {
+          ApiRegistry.registerApi(apiMetadata);
+          apiMetadataMap.put(event.getBundle().getBundleId(), apiMetadata);
+        }
+        break;
+      case BundleEvent.STOPPING:
+      case BundleEvent.STOPPED:
+        providerMetadata = providerMetadataMap.get(event.getBundle().getBundleId());
+        apiMetadata = apiMetadataMap.get(event.getBundle().getBundleId());
+        if (providerMetadata != null) {
+          ProviderRegistry.uRegisterProvider(providerMetadata);
+        }
+        if (apiMetadata != null) {
+          ApiRegistry.unRegisterApi(apiMetadata);
+        }
+        break;
+    }
+  }
+
+  /**
+   * Creates an instance of {@link ProviderMetadata} from the {@link Bundle}.
+   *
+   * @param bundle
+   * @return
+   */
+  public ProviderMetadata getProviderMetadata(Bundle bundle) {
+    ProviderMetadata metadata = null;
+    String className = getProviderMetadataClassName(bundle);
+    if (className != null && !className.isEmpty()) {
+      try {
+        Class<? extends ProviderMetadata> providerMetadataClass = bundle.loadClass(className);
+        metadata = providerMetadataClass.newInstance();
+      } catch (ClassNotFoundException e) {
+        // ignore
+      } catch (InstantiationException e) {
+        // ignore
+      } catch (IllegalAccessException e) {
+        // ignore
+      }
+    }
+    return metadata;
+  }
+
+  /**
+   * Creates an instance of {@link ApiMetadata} from the {@link Bundle}.
+   *
+   * @param bundle
+   * @return
+   */
+  public ApiMetadata getApiMetadata(Bundle bundle) {
+    ApiMetadata metadata = null;
+    String className = getApiMetadataClassName(bundle);
+    if (className != null && !className.isEmpty()) {
+      try {
+        Class<? extends ApiMetadata> apiMetadataClass = bundle.loadClass(className);
+        metadata = apiMetadataClass.newInstance();
+      } catch (ClassNotFoundException e) {
+        // ignore
+      } catch (InstantiationException e) {
+        // ignore
+      } catch (IllegalAccessException e) {
+        // ignore
+      }
+    }
+    return metadata;
+  }
+
+
+  public String getMetadataClassName(Bundle bundle, String pathToMetadata) {
+    URL resource = bundle.getEntry(pathToMetadata);
+    InputStream is = null;
+    InputStreamReader reader = null;
+    BufferedReader bufferedReader = null;
+    StringBuilder sb = new StringBuilder();
+
+    try {
+      is = resource.openStream();
+      reader = new InputStreamReader(is, "UTF-8");
+      bufferedReader = new BufferedReader(reader);
+      String line;
+      while ((line = bufferedReader.readLine()) != null) {
+        sb.append(line).append("\n");
+      }
+    } catch (Throwable e) {
+    } finally {
+      try {
+        if (reader != null)
+          reader.close();
+      } catch (Throwable e) {
+      }
+      try {
+        if (bufferedReader != null)
+          bufferedReader.close();
+      } catch (Throwable e) {
+      }
+      try {
+        is.close();
+      } catch (Throwable e) {
+      }
+
+    }
+    return sb.toString().trim();
+  }
+
+  /**
+   * Retrieves the {@link ProviderMetadata} class name for the bundle if it exists.
+   *
+   * @param bundle
+   * @return
+   */
+  public String getProviderMetadataClassName(Bundle bundle) {
+    return getMetadataClassName(bundle, "/META-INF/services/org.jclouds.providers.ProviderMetadata");
+  }
+
+  /**
+   * Retrieves the {@link ProviderMetadata} class name for the bundle if it exists.
+   *
+   * @param bundle
+   * @return
+   */
+  public String getApiMetadataClassName(Bundle bundle) {
+    return getMetadataClassName(bundle, "/META-INF/services/org.jclouds.apis.ApiMetadata");
+  }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/osgi/ProviderBundleListener.java b/core/src/main/java/org/jclouds/osgi/ProviderBundleListener.java
deleted file mode 100644
index 01d3a26..0000000
--- a/core/src/main/java/org/jclouds/osgi/ProviderBundleListener.java
+++ /dev/null
@@ -1,129 +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.osgi;
-
-import org.jclouds.providers.ProviderMetadata;
-import org.jclouds.providers.ProviderRegistry;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.BundleListener;
-
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A {@link BundleListener} that listens for {@link BundleEvent} and searches for {@link org.jclouds.providers.ProviderMetadata} in newly
- * installed Bundles. This is used as a workaround for OSGi environments where the ServiceLoader cannot cross bundle
- * boundaries.
- */
-public class ProviderBundleListener implements BundleListener {
-
-  private Map<Long, ProviderMetadata> bundleMetadataMap = new HashMap<Long, ProviderMetadata>();
-
-  @Override
-  public void bundleChanged(BundleEvent event) {
-    ProviderMetadata metadata;
-    switch (event.getType()) {
-      case BundleEvent.STARTED:
-        metadata = getProviderMetadata(event.getBundle());
-        if (metadata != null) {
-          ProviderRegistry.registerProvider(metadata);
-          bundleMetadataMap.put(event.getBundle().getBundleId(), metadata);
-        }
-        break;
-      case BundleEvent.STOPPING:
-      case BundleEvent.STOPPED:
-        metadata = bundleMetadataMap.get(event.getBundle().getBundleId());
-        if (metadata != null) {
-          ProviderRegistry.uRegisterProvider(metadata);
-        }
-        break;
-    }
-  }
-
-  /**
-   * Creates an instance of {@link ProviderMetadata} from the {@link Bundle}.
-   *
-   * @param bundle
-   * @return
-   */
-  public ProviderMetadata getProviderMetadata(Bundle bundle) {
-    ProviderMetadata metadata = null;
-    String className = getProviderMetadataClassName(bundle);
-    if (className != null && !className.isEmpty()) {
-      try {
-        Class<? extends ProviderMetadata> provideClass = bundle.loadClass(className);
-        metadata = provideClass.newInstance();
-      } catch (ClassNotFoundException e) {
-        // ignore
-      } catch (InstantiationException e) {
-        // ignore
-      } catch (IllegalAccessException e) {
-        // ignore
-      }
-    }
-    return metadata;
-  }
-
-  /**
-   * Retrieves the {@link ProviderMetadata} class name for the bundle if it exists.
-   *
-   * @param bundle
-   * @return
-   */
-  public String getProviderMetadataClassName(Bundle bundle) {
-    URL resource = bundle.getEntry("/META-INF/services/org.jclouds.providers.ProviderMetadata");
-    InputStream is = null;
-    InputStreamReader reader = null;
-    BufferedReader bufferedReader = null;
-    StringBuilder sb = new StringBuilder();
-
-    try {
-      is = resource.openStream();
-      reader = new InputStreamReader(is, "UTF-8");
-      bufferedReader = new BufferedReader(reader);
-      String line;
-      while ((line = bufferedReader.readLine()) != null) {
-        sb.append(line).append("\n");
-      }
-    } catch (Throwable e) {
-    } finally {
-      try {
-        if (reader != null)
-          reader.close();
-      } catch (Throwable e) {
-      }
-      try {
-        if (bufferedReader != null)
-          bufferedReader.close();
-      } catch (Throwable e) {
-      }
-      try {
-        is.close();
-      } catch (Throwable e) {
-      }
-
-    }
-    return sb.toString().trim();
-  }
-}
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/apis/ApisTest.java b/core/src/test/java/org/jclouds/apis/ApisTest.java
index 97a75c6..e16ca5a 100644
--- a/core/src/test/java/org/jclouds/apis/ApisTest.java
+++ b/core/src/test/java/org/jclouds/apis/ApisTest.java
@@ -43,7 +43,7 @@
       ApiMetadata apiMetadata;
       try {
          apiMetadata = Apis.withId("fake-id");
-         fail("Looking for a api with an id that doesn't exist should " + "throw an exceptoin.");
+         fail("Looking for a api with an id that doesn't exist should " + "throw an exception.");
       } catch (NoSuchElementException nsee) {
          ; // Expected
       }
diff --git a/core/src/test/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNullTest.java b/core/src/test/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNullTest.java
index 003999b..48e6daa 100644
--- a/core/src/test/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNullTest.java
+++ b/core/src/test/java/org/jclouds/location/functions/RegionToEndpointOrProviderIfNullTest.java
@@ -39,6 +39,13 @@
 public class RegionToEndpointOrProviderIfNullTest {
 
    @Test
+   public void testWhenRegionNameIsSameAsProviderName() throws SecurityException, NoSuchMethodException {
+      RegionToEndpointOrProviderIfNull fn = new RegionToEndpointOrProviderIfNull("leader", Suppliers.ofInstance(URI
+               .create("http://leader")), Suppliers.<Map<String, Supplier<URI>>>ofInstance(ImmutableMap.of("leader", Suppliers.ofInstance(URI.create("http://leaderregion")))));
+      assertEquals(fn.apply("leader"), URI.create("http://leaderregion"));
+   }
+   
+   @Test
    public void testWhenFindsRegion() throws SecurityException, NoSuchMethodException {
       RegionToEndpointOrProviderIfNull fn = new RegionToEndpointOrProviderIfNull("leader", Suppliers.ofInstance(URI
                .create("http://leader")), Suppliers.<Map<String, Supplier<URI>>>ofInstance(ImmutableMap.of("1", Suppliers.ofInstance(URI.create("http://1")))));
diff --git a/core/src/test/java/org/jclouds/providers/ProvidersTest.java b/core/src/test/java/org/jclouds/providers/ProvidersTest.java
index 0432219..d723e3a 100644
--- a/core/src/test/java/org/jclouds/providers/ProvidersTest.java
+++ b/core/src/test/java/org/jclouds/providers/ProvidersTest.java
@@ -49,7 +49,7 @@
 
       try {
          providerMetadata = Providers.withId("fake-id");
-         fail("Looking for a provider with an id that doesn't exist should " + "throw an exceptoin.");
+         fail("Looking for a provider with an id that doesn't exist should " + "throw an exception.");
       } catch (NoSuchElementException nsee) {
          ; // Expected
       }
diff --git a/drivers/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java b/drivers/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java
index 62c2318..966c94a 100644
--- a/drivers/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java
+++ b/drivers/gae/src/main/java/org/jclouds/gae/AsyncGaeHttpCommandExecutorService.java
@@ -96,7 +96,7 @@
          request = filter.filter(request);
       }
       checkRequestHasContentLengthOrChunkedEncoding(request,
-            "After filtering, the request has niether chunked encoding nor content length: " + request);
+            "After filtering, the request has neither chunked encoding nor content length: " + request);
       logger.debug("Sending request %s: %s", request.hashCode(), request.getRequestLine());
       wirePayloadIfEnabled(wire, request);
       HTTPRequest nativeRequest = convertToGaeRequest.apply(request);
diff --git a/labs/aws-elb/pom.xml b/labs/aws-elb/pom.xml
index f0feb83..faf1732 100644
--- a/labs/aws-elb/pom.xml
+++ b/labs/aws-elb/pom.xml
@@ -36,16 +36,16 @@
     <properties>
         <test.aws-elb.endpoint>https://elasticloadbalancing.us-east-1.amazonaws.com</test.aws-elb.endpoint>
         <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.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.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.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.template></test.aws-elb.compute.template>
 
       <jclouds.osgi.export>org.jclouds.aws.elb*;version="${project.version}"</jclouds.osgi.export>
       <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
@@ -134,7 +134,7 @@
                                         <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>
+                                        <test.aws-elb.compute.template>${test.aws-elb.compute.template}</test.aws-elb.compute.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/labs/carrenza-vcloud-director/pom.xml b/labs/carrenza-vcloud-director/pom.xml
index 5a45d81..490dd0f 100644
--- a/labs/carrenza-vcloud-director/pom.xml
+++ b/labs/carrenza-vcloud-director/pom.xml
@@ -39,15 +39,13 @@
         <test.carrenza-vcloud-director.build-version>464915</test.carrenza-vcloud-director.build-version>
         <test.carrenza-vcloud-director.identity>FIXME_IDENTITY</test.carrenza-vcloud-director.identity>
         <test.carrenza-vcloud-director.credential>FIXME_CREDENTIAL</test.carrenza-vcloud-director.credential>
-        <test.carrenza-vcloud-director.image-id />
-        <test.carrenza-vcloud-director.image.login-user />
-        <test.carrenza-vcloud-director.image.authenticate-sudo />
-        <test.carrenza-vcloud-director.catalog-id />
-        <test.carrenza-vcloud-director.media-id />
-        <test.carrenza-vcloud-director.vapptemplate-id />
-        <test.carrenza-vcloud-director.network-name />
-        <test.carrenza-vcloud-director.network-id />
-        <test.carrenza-vcloud-director.vdc-id />
+        <test.carrenza-vcloud-director.template></test.carrenza-vcloud-director.template>
+        <test.carrenza-vcloud-director.catalog-id></test.carrenza-vcloud-director.catalog-id>
+        <test.carrenza-vcloud-director.media-id></test.carrenza-vcloud-director.media-id>
+        <test.carrenza-vcloud-director.vapptemplate-id></test.carrenza-vcloud-director.vapptemplate-id>
+        <test.carrenza-vcloud-director.network-name></test.carrenza-vcloud-director.network-name>
+        <test.carrenza-vcloud-director.network-id></test.carrenza-vcloud-director.network-id>
+        <test.carrenza-vcloud-director.vdc-id></test.carrenza-vcloud-director.vdc-id>
 
       <jclouds.osgi.export>org.jclouds.carrenza.vcloud.director*;version="${project.version}"</jclouds.osgi.export>
       <jclouds.osgi.import>
@@ -121,9 +119,7 @@
                                         <test.carrenza-vcloud-director.build-version>${test.carrenza-vcloud-director.build-version}</test.carrenza-vcloud-director.build-version>
                                         <test.carrenza-vcloud-director.identity>${test.carrenza-vcloud-director.identity}</test.carrenza-vcloud-director.identity>
                                         <test.carrenza-vcloud-director.credential>${test.carrenza-vcloud-director.credential}</test.carrenza-vcloud-director.credential>
-                                        <test.carrenza-vcloud-director.image-id>${test.carrenza-vcloud-director.image-id}</test.carrenza-vcloud-director.image-id>
-                                        <test.carrenza-vcloud-director.image.login-user>${test.carrenza-vcloud-director.image.login-user}</test.carrenza-vcloud-director.image.login-user>
-                                        <test.carrenza-vcloud-director.image.authenticate-sudo>${test.carrenza-vcloud-director.image.authenticate-sudo}</test.carrenza-vcloud-director.image.authenticate-sudo>
+                                        <test.carrenza-vcloud-director.template>${test.carrenza-vcloud-director.template}</test.carrenza-vcloud-director.template>
 					                    <test.carrenza-vcloud-director.catalog-id>${test.carrenza-vcloud-director.catalog-id}</test.carrenza-vcloud-director.catalog-id>
 					                    <test.carrenza-vcloud-director.media-id>${test.carrenza-vcloud-director.media-id}</test.carrenza-vcloud-director.media-id>
 					                    <test.carrenza-vcloud-director.vapptemplate-id>${test.carrenza-vcloud-director.vapptemplate-id}</test.carrenza-vcloud-director.vapptemplate-id>
diff --git a/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/domain/CDMIObject.java b/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/domain/CDMIObject.java
index 5950865..52faa8e 100644
--- a/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/domain/CDMIObject.java
+++ b/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/domain/CDMIObject.java
@@ -29,137 +29,144 @@
 /**
  * The base type for all objects in the CDMI model.
  * 
- * @author Adrian Cole
+ * @author Kenneth Nagin
  */
 public class CDMIObject {
 
-   public static Builder<?> builder() {
-      return new ConcreteBuilder();
-   }
+	public static Builder<?> builder() {
+		return new ConcreteBuilder();
+	}
 
-   public Builder<?> toBuilder() {
-      return builder().fromCDMIObject(this);
-   }
+	public Builder<?> toBuilder() {
+		return builder().fromCDMIObject(this);
+	}
 
-   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
-   }
+	private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+	}
 
-   public static abstract class Builder<B extends Builder<B>> {
-      private String objectID;
-      private String objectType;
-      private String objectName;
+	public static abstract class Builder<B extends Builder<B>> {
+		private String objectID;
+		private String objectType;
+		private String objectName;
 
-      @SuppressWarnings("unchecked")
-      protected B self() {
-         return (B) this;
-      }
+		@SuppressWarnings("unchecked")
+		protected B self() {
+			return (B) this;
+		}
 
-      /**
-       * @see CDMIObject#getObjectID()
-       */
-      public B objectID(String objectID) {
-         this.objectID = objectID;
-         return self();
-      }
+		/**
+		 * @see CDMIObject#getObjectID()
+		 */
+		public B objectID(String objectID) {
+			this.objectID = objectID;
+			return self();
+		}
 
-      /**
-       * @see CDMIObject#getObjectType()
-       */
-      public B objectType(String objectType) {
-         this.objectType = objectType;
-         return self();
-      }
+		/**
+		 * @see CDMIObject#getObjectType()
+		 */
+		public B objectType(String objectType) {
+			this.objectType = objectType;
+			return self();
+		}
 
-      /**
-       * @see CDMIObject#getObjectName()
-       */
-      public B objectName(String objectName) {
-         this.objectName = objectName;
-         return self();
-      }
+		/**
+		 * @see CDMIObject#getObjectName()
+		 */
+		public B objectName(String objectName) {
+			this.objectName = objectName;
+			return self();
+		}
 
-      public CDMIObject build() {
-         return new CDMIObject(this);
-      }
+		public CDMIObject build() {
+			return new CDMIObject(this);
+		}
 
-      protected B fromCDMIObject(CDMIObject in) {
-         return objectID(in.getObjectID()).objectType(in.getObjectType()).objectName(in.getObjectName());
-      }
-   }
+		protected B fromCDMIObject(CDMIObject in) {
+			return objectID(in.getObjectID()).objectType(in.getObjectType())
+					.objectName(in.getObjectName());
+		}
+	}
 
-   private final String objectID;
-   private final String objectType;
-   private final String objectName;
+	private final String objectID;
+	private final String objectType;
+	private final String objectName;
 
-   protected CDMIObject(Builder<?> builder) {
-      this.objectID = checkNotNull(builder.objectID, "objectID");
-      this.objectType = checkNotNull(builder.objectType, "objectType");
-      this.objectName = builder.objectName;
-   }
+	protected CDMIObject(Builder<?> builder) {
+		this.objectID = checkNotNull(builder.objectID, "objectID");
+		this.objectType = checkNotNull(builder.objectType, "objectType");
+		this.objectName = builder.objectName;
+	}
 
-   /**
-    * Object ID of the object <br/>
-    * Every object stored within a CDMI-compliant system shall have a globally unique object
-    * identifier (ID) assigned at creation time. The CDMI object ID is a string with requirements
-    * for how it is generated and how it obtains its uniqueness. Each offering that implements CDMI
-    * is able to produce these identifiers without conflicting with other offerings.
-    */
-   public String getObjectID() {
-      return objectID;
-   }
+	/**
+	 * Object ID of the object <br/>
+	 * Every object stored within a CDMI-compliant system shall have a globally
+	 * unique object identifier (ID) assigned at creation time. The CDMI object
+	 * ID is a string with requirements for how it is generated and how it
+	 * obtains its uniqueness. Each offering that implements CDMI is able to
+	 * produce these identifiers without conflicting with other offerings.
+	 * 
+	 * note: CDMI Servers do not always support ObjectID tags, however
+	 * downstream jclouds code does not handle null so we return a empty String
+	 * instead.
+	 */
+	public String getObjectID() {
+		return (objectID == null) ? "" : objectID;
+	}
 
-   /**
-    * 
-    * type of the object
-    */
-   public String getObjectType() {
-      return objectType;
-   }
+	/**
+	 * 
+	 * type of the object
+	 */
+	public String getObjectType() {
+		return objectType;
+	}
 
-   /**
-    * For objects in a container, the objectName field shall be returned. For objects not in a
-    * container (objects that are only accessible by ID), the objectName field shall not be
-    * returned.
-    * 
-    * Name of the object
-    */
-   @Nullable
-   public String getObjectName() {
-      return objectName;
-   }
+	/**
+	 * For objects in a container, the objectName field shall be returned. For
+	 * objects not in a container (objects that are only accessible by ID), the
+	 * objectName field shall not be returned.
+	 * 
+	 * Name of the object
+	 */
+	@Nullable
+	public String getObjectName() {
+		return (objectName == null) ? "" : objectName;
+	}
 
-   @Override
-   public boolean equals(Object o) {
-      if (this == o)
-         return true;
-      if (o == null || getClass() != o.getClass())
-         return false;
-      CDMIObject that = CDMIObject.class.cast(o);
-      return equal(this.objectID, that.objectID) && equal(this.objectName, that.objectName)
-               && equal(this.objectType, that.objectType);
-   }
+	@Override
+	public boolean equals(Object o) {
+		if (this == o)
+			return true;
+		if (o == null || getClass() != o.getClass())
+			return false;
+		CDMIObject that = CDMIObject.class.cast(o);
+		return equal(this.objectID, that.objectID)
+				&& equal(this.objectName, that.objectName)
+				&& equal(this.objectType, that.objectType);
+	}
 
-   public boolean clone(Object o) {
-      if (this == o)
-         return false;
-      if (o == null || getClass() != o.getClass())
-         return false;
-      CDMIObject that = CDMIObject.class.cast(o);
-      return equal(this.objectType, that.objectType);
-   }
+	public boolean clone(Object o) {
+		if (this == o)
+			return false;
+		if (o == null || getClass() != o.getClass())
+			return false;
+		CDMIObject that = CDMIObject.class.cast(o);
+		return equal(this.objectType, that.objectType);
+	}
 
-   @Override
-   public int hashCode() {
-      return Objects.hashCode(objectID, objectName, objectType);
-   }
+	@Override
+	public int hashCode() {
+		return Objects.hashCode(objectID, objectName, objectType);
+	}
 
-   @Override
-   public String toString() {
-      return string().toString();
-   }
+	@Override
+	public String toString() {
+		return string().toString();
+	}
 
-   protected ToStringHelper string() {
-      return Objects.toStringHelper("").add("objectID", objectID).add("objectName", objectName).add("objectType",
-               objectType);
-   }
+	protected ToStringHelper string() {
+		return Objects.toStringHelper("").add("objectID", objectID)
+				.add("objectName", objectName).add("objectType", objectType);
+	}
 }
diff --git a/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/domain/Container.java b/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/domain/Container.java
index f0d1aff..24567f5 100644
--- a/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/domain/Container.java
+++ b/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/domain/Container.java
@@ -21,9 +21,15 @@
 import static com.google.common.base.Objects.equal;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.jclouds.domain.JsonBall;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableMap;
@@ -32,103 +38,196 @@
 
 /**
  * 
- * @author Adrian Cole
+ * @author Kenneth Nagin
  */
 public class Container extends CDMIObject {
 
-   public static Builder<?> builder() {
-      return new ConcreteBuilder();
-   }
+	public static Builder<?> builder() {
+		return new ConcreteBuilder();
+	}
 
-   @Override
-   public Builder<?> toBuilder() {
-      return builder().fromContainer(this);
-   }
+	@Override
+	public Builder<?> toBuilder() {
+		return builder().fromContainer(this);
+	}
 
-   public static class Builder<B extends Builder<B>> extends CDMIObject.Builder<B> {
+	public static class Builder<B extends Builder<B>> extends
+			CDMIObject.Builder<B> {
 
-      private Set<String> children = ImmutableSet.of();
-      private Map<String, String> metadata = Maps.newHashMap();
+		private Set<String> children = ImmutableSet.of();
+		private Map<String, JsonBall> metadata = Maps.newHashMap();
 
-      /**
-       * @see Container#getChildren()
-       */
-      public B children(String... children) {
-         return children(ImmutableSet.copyOf(checkNotNull(children, "children")));
-      }
+		/**
+		 * @see Container#getChildren()
+		 */
+		public B children(String... children) {
+			return children(ImmutableSet.copyOf(checkNotNull(children,
+					"children")));
+		}
 
-      /**
-       * @see Container#getChildren()
-       */
-      public B children(Set<String> children) {
-         this.children = ImmutableSet.copyOf(checkNotNull(children, "children"));
-         return self();
-      }
+		/**
+		 * @see Container#getChildren()
+		 */
+		public B children(Set<String> children) {
+			this.children = ImmutableSet.copyOf(checkNotNull(children,
+					"children"));
+			return self();
+		}
 
-      /**
-       * @see Container#getMetadata()
-       */
-      public B metadata(Map<String, String> metadata) {
-         this.metadata = ImmutableMap.copyOf(checkNotNull(metadata, "metadata"));
-         return self();
-      }
+		/**
+		 * @see Container#getMetadata()
+		 */
+		public B metadata(Map<String, JsonBall> metadata) {
+			this.metadata = ImmutableMap.copyOf(checkNotNull(metadata,
+					"metadata"));
+			return self();
+		}
 
-      @Override
-      public Container build() {
-         return new Container(this);
-      }
+		@Override
+		public Container build() {
+			return new Container(this);
+		}
 
-      public B fromContainer(Container in) {
-         return fromCDMIObject(in).children(in.getChildren()).metadata(in.getMetadata());
-      }
-   }
+		public B fromContainer(Container in) {
+			return fromCDMIObject(in).children(in.getChildren()).metadata(
+					in.getMetadata());
+		}
+	}
 
-   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
-   }
+	private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+	}
 
-   private final Set<String> children;
-   private final Map<String, String> metadata;
+	private final Set<String> children;
+	private final Map<String, JsonBall> metadata;
 
-   protected Container(Builder<?> builder) {
-      super(builder);
-      this.children = ImmutableSet.copyOf(checkNotNull(builder.children, "children"));
-      this.metadata = ImmutableMap.copyOf(checkNotNull(builder.metadata, "metadata"));
-   }
+	private Map<String, String> userMetaDataIn;
+	private Map<String, String> systemMetaDataIn;
+	private List<Map<String, String>> aclMetaDataIn;
 
-   /**
-    * Names of the children objects in the container object. Child container objects end with "/".
-    */
-   public Set<String> getChildren() {
-      return children;
-   }
+	protected Container(Builder<?> builder) {
+		super(builder);
+		this.children = ImmutableSet.copyOf(checkNotNull(builder.children,
+				"children"));
+		this.metadata = ImmutableMap.copyOf(checkNotNull(builder.metadata,
+				"metadata"));
+	}
 
-   /**
-    * Metadata for the container object. This field includes any user and data system metadata
-    * specified in the request body metadata field, along with storage system metadata generated by
-    * the cloud storage system.
-    */
-   public Map<String, String> getMetadata() {
-      return metadata;
-   }
+	/**
+	 * Names of the children objects in the container object. Child container
+	 * objects end with "/".
+	 */
+	public Set<String> getChildren() {
+		return children;
+	}
 
-   @Override
-   public boolean equals(Object o) {
-      if (this == o)
-         return true;
-      if (o == null || getClass() != o.getClass())
-         return false;
-      Container that = Container.class.cast(o);
-      return super.equals(that) && equal(this.children, that.children) && equal(this.metadata, that.metadata);
-   }
+	/**
+	 * Metadata for the container object. This field includes any user and
+	 * system metadata specified in the request body metadata field, along with
+	 * storage system metadata generated by the cloud storage system.
+	 */
+	public Map<String, JsonBall> getMetadata() {
+		return metadata;
+	}
 
-   @Override
-   public int hashCode() {
-      return Objects.hashCode(super.hashCode(), children, metadata);
-   }
+	/**
+	 * Parse Metadata for the container object from the original JsonBall.
+	 * System metadata data is prefixed with cdmi. System ACL metadata data is
+	 * prefixed with cdmi_acl
+	 * 
+	 */
+	private void parseMetadata() {
+		userMetaDataIn = new HashMap<String, String>();
+		systemMetaDataIn = new HashMap<String, String>();
+		aclMetaDataIn = new ArrayList<Map<String, String>>();
+		Iterator<String> keys = metadata.keySet().iterator();
+		while (keys.hasNext()) {
+			String key = keys.next();
+			JsonBall value = metadata.get(key);
+			if (key.startsWith("cdmi")) {
+				if (key.matches("cdmi_acl")) {
+					String[] cdmi_acl_array = value.toString().split("[{}]");
+					for (int i = 0; i < cdmi_acl_array.length; i++) {
+						if (!(cdmi_acl_array[i].startsWith("[")
+								|| cdmi_acl_array[i].startsWith("]") || cdmi_acl_array[i]
+								.startsWith(","))) {
+							HashMap<String, String> aclMap = new HashMap<String, String>();
+							String[] cdmi_acl_member = cdmi_acl_array[i]
+									.split(",");
+							for (String s : cdmi_acl_member) {
+								String cdmi_acl_key = s.substring(0,
+										s.indexOf(":"));
+								String cdmi_acl_value = s.substring(s
+										.indexOf(":") + 1);
+								cdmi_acl_value.replace('"', ' ').trim();
+								aclMap.put(cdmi_acl_key, cdmi_acl_value);
+							}
+							aclMetaDataIn.add(aclMap);
+						}
+					}
+				} else {
+					systemMetaDataIn.put(key, value.toString()
+							.replace('"', ' ').trim());
+				}
+			} else {
+				userMetaDataIn.put(key, value.toString().replace('"', ' ')
+						.trim());
+			}
+		}
+	}
 
-   @Override
-   public ToStringHelper string() {
-      return super.string().add("children", children).add("metadata", metadata);
-   }
+	/**
+	 * Get User Metadata for the container object. This field includes any user
+	 * metadata
+	 */
+	public Map<String, String> getUserMetadata() {
+		if (userMetaDataIn == null) {
+			parseMetadata();
+		}
+		return userMetaDataIn;
+	}
+
+	/**
+	 * Get System Metadata for the container object excluding ACL related
+	 * metadata
+	 */
+	public Map<String, String> getSystemMetadata() {
+		if (systemMetaDataIn == null) {
+			parseMetadata();
+		}
+		return systemMetaDataIn;
+	}
+
+	/**
+	 * Get System Metadata for the container object excluding ACL related
+	 * metadata
+	 */
+	public List<Map<String, String>> getACLMetadata() {
+		if (aclMetaDataIn == null) {
+			parseMetadata();
+		}
+		return aclMetaDataIn;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (this == o)
+			return true;
+		if (o == null || getClass() != o.getClass())
+			return false;
+		Container that = Container.class.cast(o);
+		return super.equals(that) && equal(this.children, that.children)
+				&& equal(this.metadata, that.metadata);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hashCode(super.hashCode(), children, metadata);
+	}
+
+	@Override
+	public ToStringHelper string() {
+		return super.string().add("children", children)
+				.add("metadata", metadata);
+	}
 
 }
diff --git a/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/features/ContainerAsyncClient.java b/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/features/ContainerAsyncClient.java
index 1bbf725..f738dfc 100644
--- a/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/features/ContainerAsyncClient.java
+++ b/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/features/ContainerAsyncClient.java
@@ -19,9 +19,12 @@
 package org.jclouds.snia.cdmi.v1.features;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.rest.annotations.ExceptionParser;
@@ -40,7 +43,7 @@
  * Container Object Resource Operations
  * 
  * @see ContainerClient
- * @author Adrian Cole
+ * @author Kenneth Nagin
  * @see <a href="http://www.snia.org/cdmi">api doc</a>
  */
 @SkipEncoding( { '/', '=' })
@@ -56,5 +59,25 @@
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
    @Path("/{containerName}/")
    ListenableFuture<Container> getContainer(@PathParam("containerName") String containerName);
+   
+   /**
+    * @see ContainerClient#createContainer
+    */
+   @PUT
+   @Consumes( { ObjectTypes.CONTAINER, MediaType.APPLICATION_JSON })
+   @Produces( { ObjectTypes.CONTAINER})
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)   
+   @Path("/{containerName}/")
+   ListenableFuture<Container> createContainer(@PathParam("containerName") String containerName);
+
+   /**
+    * @see ContainerClient#createContainer()
+    */
+   @DELETE
+//   @Consumes( { ObjectTypes.CONTAINER, MediaType.APPLICATION_JSON })
+   @Consumes( MediaType.APPLICATION_JSON )
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   @Path("/{containerName}/")
+   ListenableFuture<Void> deleteContainer(@PathParam("containerName") String containerName);
 
 }
diff --git a/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/features/ContainerClient.java b/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/features/ContainerClient.java
index 76df5c7..2bd3b8e 100644
--- a/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/features/ContainerClient.java
+++ b/labs/cdmi/src/main/java/org/jclouds/snia/cdmi/v1/features/ContainerClient.java
@@ -27,12 +27,15 @@
  * Container Object Resource Operations
  * 
  * @see ContainerAsyncClient
- * @author Adrian Cole
+ * @author Kenneth Nagin
  * @see <a href="http://www.snia.org/cdmi">api doc</a>
  */
 @Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
 public interface ContainerClient {
+	Container createContainer(String containerName);
 
-   Container getContainer(String containerName);
+	Container getContainer(String containerName);
+
+	void deleteContainer(String containerName);
 
 }
diff --git a/labs/cdmi/src/test/java/org/jclouds/snia/cdmi/v1/features/ContainerClientLiveTest.java b/labs/cdmi/src/test/java/org/jclouds/snia/cdmi/v1/features/ContainerClientLiveTest.java
index 1ee33f4..012a527 100644
--- a/labs/cdmi/src/test/java/org/jclouds/snia/cdmi/v1/features/ContainerClientLiveTest.java
+++ b/labs/cdmi/src/test/java/org/jclouds/snia/cdmi/v1/features/ContainerClientLiveTest.java
@@ -21,6 +21,10 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
 import org.jclouds.snia.cdmi.v1.ObjectTypes;
 import org.jclouds.snia.cdmi.v1.domain.Container;
 import org.jclouds.snia.cdmi.v1.internal.BaseCDMIClientLiveTest;
@@ -28,21 +32,49 @@
 
 /**
  * 
- * @author Adrian Cole
+ * @author Kenneth Nagin
  */
 @Test(groups = "live", testName = "ContainerClientLiveTest")
 public class ContainerClientLiveTest extends BaseCDMIClientLiveTest {
-
    @Test
-   public void testGetContainer() throws Exception {
+   public void testCreateContainer() throws Exception {
+      String pContainerName = "MyContainer" + System.currentTimeMillis();
       ContainerClient client = cdmiContext.getApi().getContainerClient();
-      Container container = client.getContainer("TODO: figure out how to list containers");
+      Logger.getAnonymousLogger().info("createContainer: " + pContainerName);
+      Container container = client.createContainer(pContainerName);
       assertNotNull(container);
+      System.out.println(container);
+      Logger.getAnonymousLogger().info("getContainer: " + pContainerName);
+      container = client.getContainer(pContainerName);
+      assertNotNull(container);
+      System.out.println(container);
       assertEquals(container.getObjectType(), ObjectTypes.CONTAINER);
       assertNotNull(container.getObjectID());
       assertNotNull(container.getObjectName());
+      assertEquals(container.getObjectName(), pContainerName + "/");
       assertNotNull(container.getChildren());
+      assertEquals(container.getChildren().isEmpty(), true);
+      System.out.println("Children: " + container.getChildren());
       assertNotNull(container.getMetadata());
+      assertNotNull(container.getUserMetadata());
+      System.out.println("UserMetaData: " + container.getUserMetadata());
+      assertNotNull(container.getSystemMetadata());
+      System.out.println("SystemMetaData: " + container.getSystemMetadata());
+      assertNotNull(container.getACLMetadata());
+      List<Map<String, String>> aclMetadataOut = container.getACLMetadata();
+      System.out.println("ACLMetaData: ");
+      for (Map<String, String> aclMap : aclMetadataOut) {
+         System.out.println(aclMap);
+      }
+      container = client.getContainer("/");
+      System.out.println("root container: " + container);
+      assertEquals(container.getChildren().contains(pContainerName + "/"), true);
+      Logger.getAnonymousLogger().info("deleteContainer: " + pContainerName);
+      client.deleteContainer(pContainerName);
+      container = client.getContainer("/");
+      System.out.println("root container: " + container);
+      assertEquals(container.getChildren().contains(pContainerName + "/"), false);
+
    }
 
 }
diff --git a/labs/cdmi/src/test/java/org/jclouds/snia/cdmi/v1/parse/ParseContainerTest.java b/labs/cdmi/src/test/java/org/jclouds/snia/cdmi/v1/parse/ParseContainerTest.java
index 14fea21..2cd3ef4 100644
--- a/labs/cdmi/src/test/java/org/jclouds/snia/cdmi/v1/parse/ParseContainerTest.java
+++ b/labs/cdmi/src/test/java/org/jclouds/snia/cdmi/v1/parse/ParseContainerTest.java
@@ -21,6 +21,7 @@
 import javax.ws.rs.Consumes;
 import javax.ws.rs.core.MediaType;
 
+import org.jclouds.domain.JsonBall;
 import org.jclouds.json.BaseItemParserTest;
 import org.jclouds.snia.cdmi.v1.domain.Container;
 import org.testng.annotations.Test;
@@ -30,7 +31,7 @@
 
 /**
  * 
- * @author Adrian Cole
+ * @author Kenneth Nagin
  */
 @Test(groups = "unit", testName = "ParseContainerTest")
 public class ParseContainerTest extends BaseItemParserTest<Container> {
@@ -47,8 +48,8 @@
                       .objectType("application/cdmi-container")
                       .objectID("00007E7F00102E230ED82694DAA975D2")
                       .objectName("MyContainer/")
-                      .metadata(ImmutableMap.<String, String>builder()
-                          .put("cdmi_size", "83")
+                      .metadata(ImmutableMap.<String, JsonBall>builder()
+                          .put("cdmi_size", new JsonBall("\"83\""))
                           .build())
                       .children(ImmutableSet.<String>builder()
                           .add("MyDataObject.txt")
diff --git a/labs/elb/pom.xml b/labs/elb/pom.xml
index 3e8b49f..48649e2 100644
--- a/labs/elb/pom.xml
+++ b/labs/elb/pom.xml
@@ -37,16 +37,16 @@
         <test.elb.zone>us-east-1a</test.elb.zone>
         <test.elb.endpoint>https://elasticloadbalancing.us-east-1.amazonaws.com</test.elb.endpoint>
         <test.elb.api-version>2010-07-01</test.elb.api-version>
-        <test.elb.build-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>aws-ec2</test.elb.compute.provider>
         <test.elb.compute.endpoint>https://ec2.us-east-1.amazonaws.com</test.elb.compute.endpoint>
         <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.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.template></test.elb.compute.template>
 
       <jclouds.osgi.export>org.jclouds.elb*;version="${project.version}"</jclouds.osgi.export>
       <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
@@ -134,7 +134,7 @@
                                         <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>
+                                        <test.elb.compute.template>${test.elb.compute.template}</test.elb.compute.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/labs/elb/src/test/java/org/jclouds/elb/ELBAsyncClientTest.java b/labs/elb/src/test/java/org/jclouds/elb/ELBAsyncClientTest.java
index bd9a10a..85486c1 100644
--- a/labs/elb/src/test/java/org/jclouds/elb/ELBAsyncClientTest.java
+++ b/labs/elb/src/test/java/org/jclouds/elb/ELBAsyncClientTest.java
@@ -23,9 +23,6 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
-import javax.inject.Named;
-
-import org.jclouds.Constants;
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.aws.filters.FormSigner;
 import org.jclouds.date.DateService;
@@ -162,8 +159,7 @@
       }
 
       @Override
-      protected String provideTimeStamp(final DateService dateService,
-            @Named(Constants.PROPERTY_SESSION_INTERVAL) int expiration) {
+      protected String provideTimeStamp(final DateService dateService) {
          return "2009-11-08T15:54:08.897Z";
       }
    }
diff --git a/labs/glesys/pom.xml b/labs/glesys/pom.xml
index 220f66f..bc2b677 100644
--- a/labs/glesys/pom.xml
+++ b/labs/glesys/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.glesys.endpoint>https://api.glesys.com</test.glesys.endpoint>
     <test.glesys.api-version>1</test.glesys.api-version>
-    <test.glesys.build-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.login-user />
-    <test.glesys.image.authenticate-sudo />
-
+    <test.glesys.template></test.glesys.template>
     <jclouds.osgi.export>org.jclouds.glesys*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
   </properties>
@@ -102,9 +99,7 @@
                     <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>
-                    <test.glesys.image.login-user>${test.glesys.image.login-user}</test.glesys.image.login-user>
-                    <test.glesys.image.authenticate-sudo>${test.glesys.image.authenticate-sudo}</test.glesys.image.authenticate-sudo>
+                    <test.glesys.template>${test.glesys.template}</test.glesys.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/labs/greenqloud-compute/pom.xml b/labs/greenqloud-compute/pom.xml
new file mode 100644
index 0000000..698f58e
--- /dev/null
+++ b/labs/greenqloud-compute/pom.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.jclouds</groupId>
+        <artifactId>jclouds-project</artifactId>
+        <version>1.5.0-SNAPSHOT</version>
+        <relativePath>../../project/pom.xml</relativePath>
+    </parent>
+    <groupId>org.jclouds.labs</groupId>
+    <artifactId>greenqloud-compute</artifactId>
+    <name>jclouds Greenqloud compute provider</name>
+    <description>Compute implementation targeted to Greenqloud Web Services</description>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <test.greenqloud-compute.endpoint>https://api.greenqloud.com</test.greenqloud-compute.endpoint>
+        <test.greenqloud-compute.api-version>2010-06-15</test.greenqloud-compute.api-version>
+        <test.greenqloud-compute.build-version></test.greenqloud-compute.build-version>
+        <test.greenqloud-compute.identity>${test.greenqloud.identity}</test.greenqloud-compute.identity>
+        <test.greenqloud-compute.credential>${test.greenqloud.credential}</test.greenqloud-compute.credential>
+        <test.greenqloud-compute.template></test.greenqloud-compute.template>
+        <test.greenqloud-compute.ebs-template>osFamily=UBUNTU,osVersionMatches=1[012].[01][04],os64Bit=true</test.greenqloud-compute.ebs-template>
+
+        <jclouds.osgi.export>org.jclouds.greenqloud.compute*;version="${project.version}"</jclouds.osgi.export>
+        <jclouds.osgi.import>
+          org.jclouds.compute.internal;version="${project.version}",
+          org.jclouds.rest.internal;version="${project.version}",
+          org.jclouds*;version="${project.version}",
+          *
+        </jclouds.osgi.import>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jclouds.api</groupId>
+            <artifactId>ec2</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds.api</groupId>
+            <artifactId>ec2</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds</groupId>
+            <artifactId>jclouds-compute</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds</groupId>
+            <artifactId>jclouds-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds.driver</groupId>
+            <artifactId>jclouds-log4j</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds.driver</groupId>
+            <artifactId>jclouds-sshj</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>live</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>integration</id>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <threadCount>1</threadCount>
+                                    <systemPropertyVariables>
+                                        <test.greenqloud-compute.endpoint>${test.greenqloud-compute.endpoint}</test.greenqloud-compute.endpoint>
+                                        <test.greenqloud-compute.api-version>${test.greenqloud-compute.api-version}</test.greenqloud-compute.api-version>
+                                        <test.greenqloud-compute.build-version>${test.greenqloud-compute.build-version}</test.greenqloud-compute.build-version>
+                                        <test.greenqloud-compute.identity>${test.greenqloud-compute.identity}</test.greenqloud-compute.identity>
+                                        <test.greenqloud-compute.credential>${test.greenqloud-compute.credential}</test.greenqloud-compute.credential>
+                                        <test.greenqloud-compute.template>${test.greenqloud-compute.template}</test.greenqloud-compute.template>
+                                    </systemPropertyVariables>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
+
diff --git a/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/GreenQloudComputeProviderMetadata.java b/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/GreenQloudComputeProviderMetadata.java
new file mode 100644
index 0000000..00cd0cd
--- /dev/null
+++ b/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/GreenQloudComputeProviderMetadata.java
@@ -0,0 +1,101 @@
+/**
+ * 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.greenqloud.compute;
+
+import static org.jclouds.location.reference.LocationConstants.ISO3166_CODES;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.ec2.EC2ApiMetadata;
+import org.jclouds.ec2.compute.config.EC2ResolveImagesModule;
+import org.jclouds.ec2.config.EC2RestClientModule;
+import org.jclouds.greenqloud.compute.config.GreenQloudComputeComputeServiceContextModule;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+/**
+ * Implementation of {@link org.jclouds.providers.ProviderMetadata} for Greenqloud Compute Cloud.
+ * 
+ * @author Adrian Cole
+ */
+public class GreenQloudComputeProviderMetadata extends BaseProviderMetadata {
+   
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 7625722444851538962L;
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   @Override
+   public Builder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
+   
+   public GreenQloudComputeProviderMetadata() {
+      super(builder());
+   }
+
+   public GreenQloudComputeProviderMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = new Properties();
+      properties.setProperty(PROPERTY_REGIONS, "is-1");
+      properties.setProperty(PROPERTY_REGION + ".is-1." + ISO3166_CODES, "IS-1");
+      return properties;
+   }
+   
+   public static class Builder extends BaseProviderMetadata.Builder {
+
+      protected Builder(){
+         id("greenqloud-compute")
+         .name("Greenqloud Compute Cloud")
+               .apiMetadata(
+                     new EC2ApiMetadata().toBuilder()
+                            .defaultModules(ImmutableSet.<Class<? extends Module>>of(EC2RestClientModule.class, EC2ResolveImagesModule.class, GreenQloudComputeComputeServiceContextModule.class))
+                            .build())
+         .homepage(URI.create("http://www.greenqloud.com"))
+         .console(URI.create("https://manage.greenqloud.com"))
+         .linkedServices("greenqloud-compute", "greenqloud-storage")
+         .iso3166Codes("IS-1")
+         .endpoint("https://api.greenqloud.com")
+         .defaultProperties(GreenQloudComputeProviderMetadata.defaultProperties());
+      }
+
+      @Override
+      public GreenQloudComputeProviderMetadata build() {
+         return new GreenQloudComputeProviderMetadata(this);
+      }
+      
+      @Override
+      public Builder fromProviderMetadata(
+            ProviderMetadata in) {
+         super.fromProviderMetadata(in);
+         return this;
+      }
+   }
+}
\ No newline at end of file
diff --git a/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/config/GreenQloudComputeComputeServiceContextModule.java b/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/config/GreenQloudComputeComputeServiceContextModule.java
new file mode 100644
index 0000000..6bff01b
--- /dev/null
+++ b/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/config/GreenQloudComputeComputeServiceContextModule.java
@@ -0,0 +1,37 @@
+/**
+ * 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.greenqloud.compute.config;
+
+import org.jclouds.ec2.compute.config.EC2ComputeServiceContextModule;
+import org.jclouds.ec2.compute.strategy.ReviseParsedImage;
+import org.jclouds.greenqloud.compute.strategy.GreenQloudComputeReviseParsedImage;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class GreenQloudComputeComputeServiceContextModule extends EC2ComputeServiceContextModule {
+
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(ReviseParsedImage.class).to(GreenQloudComputeReviseParsedImage.class);
+   }
+
+}
diff --git a/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/strategy/GreenQloudComputeReviseParsedImage.java b/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/strategy/GreenQloudComputeReviseParsedImage.java
new file mode 100644
index 0000000..1d54b88
--- /dev/null
+++ b/labs/greenqloud-compute/src/main/java/org/jclouds/greenqloud/compute/strategy/GreenQloudComputeReviseParsedImage.java
@@ -0,0 +1,65 @@
+/**
+ * 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.greenqloud.compute.strategy;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.util.ComputeServiceUtils;
+import org.jclouds.ec2.compute.strategy.ReviseParsedImage;
+import org.jclouds.logging.Logger;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class GreenQloudComputeReviseParsedImage implements ReviseParsedImage {
+   public static final Pattern DEFAULT_PATTERN = Pattern.compile("(([^ ]*) ([0-9.]+) ?.*)");
+
+   @javax.annotation.Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final Map<OsFamily, Map<String, String>> osVersionMap;
+
+   @Inject
+   public GreenQloudComputeReviseParsedImage(Map<OsFamily, Map<String, String>> osVersionMap) {
+      this.osVersionMap = osVersionMap;
+   }
+
+   public void reviseParsedImage(org.jclouds.ec2.domain.Image from, ImageBuilder builder, OsFamily family,
+            OperatingSystem.Builder osBuilder) {
+      if (from.getDescription() != null)
+         osBuilder.is64Bit(from.getDescription().contains("64-bit"));
+
+      Matcher matcher = DEFAULT_PATTERN.matcher(from.getDescription());
+      if (matcher.find() && matcher.groupCount() >= 3) {
+         osBuilder.version(ComputeServiceUtils.parseVersionOrReturnEmptyString(family, matcher.group(3), osVersionMap));
+      }
+   }
+}
\ No newline at end of file
diff --git a/labs/greenqloud-compute/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/labs/greenqloud-compute/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
new file mode 100644
index 0000000..ddcff31
--- /dev/null
+++ b/labs/greenqloud-compute/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
@@ -0,0 +1 @@
+org.jclouds.greenqloud.compute.GreenQloudComputeProviderMetadata
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeComputeServiceLiveTest.java
similarity index 63%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeComputeServiceLiveTest.java
index 771a1be..fc332ec 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeComputeServiceLiveTest.java
@@ -16,13 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.compute;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.ec2.compute.EC2ComputeServiceLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "GreenQloudComputeComputeServiceLiveTest")
+public class GreenQloudComputeComputeServiceLiveTest extends EC2ComputeServiceLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public GreenQloudComputeComputeServiceLiveTest() {
+      provider = "greenqloud-compute";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeProviderTest.java
similarity index 63%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeProviderTest.java
index 771a1be..134d6c7 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeProviderTest.java
@@ -16,13 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.compute;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.ec2.EC2ApiMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+@Test(groups = "unit", testName = "GreenQloudComputeProviderTest")
+public class GreenQloudComputeProviderTest extends BaseProviderMetadataTest {
 
-   void startPool() throws RunNodesException;
+   public GreenQloudComputeProviderTest() {
+      super(new GreenQloudComputeProviderMetadata(), new EC2ApiMetadata());
+   }
 
-}
+}
\ No newline at end of file
diff --git a/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeTemplateBuilderLiveTest.java b/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeTemplateBuilderLiveTest.java
new file mode 100644
index 0000000..d233f05
--- /dev/null
+++ b/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/GreenQloudComputeTemplateBuilderLiveTest.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.greenqloud.compute;
+
+import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.jclouds.aws.util.AWSUtils;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.OsFamilyVersion64Bit;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.internal.BaseTemplateBuilderLiveTest;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "GreenQloudComputeTemplateBuilderLiveTest")
+public class GreenQloudComputeTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest {
+
+   public GreenQloudComputeTemplateBuilderLiveTest() {
+      provider = "greenqloud-compute";
+   }
+
+   @Override
+   protected Predicate<OsFamilyVersion64Bit> defineUnsupportedOperatingSystems() {
+      return Predicates.not(new Predicate<OsFamilyVersion64Bit>() {
+
+         @Override
+         public boolean apply(OsFamilyVersion64Bit input) {
+            switch (input.family) {
+               case UBUNTU:
+                  return (input.version.equals("") || input.version.equals("10.04") || input.version.equals("11.04") || input.version
+                           .equals("11.10"))
+                           && input.is64Bit;
+               case DEBIAN:
+                  return (input.version.equals("") || input.version.equals("6.0")) && input.is64Bit;
+               case CENTOS:
+                  return (input.version.equals("") || input.version.equals("5.5") || input.version.equals("5.6") || input.version
+                           .equals("6.0"))
+                           && input.is64Bit;
+               case FEDORA:
+                  return input.version.equals("") && input.is64Bit;
+               default:
+                  return false;
+            }
+         }
+
+      });
+   }
+
+   @Test
+   public void testDefaultTemplateBuilder() throws IOException {
+      Template defaultTemplate = view.getComputeService().templateBuilder().build();
+      assert (defaultTemplate.getImage().getProviderId().startsWith("qmi-")) : defaultTemplate;
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "11.10");
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
+      assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs");
+      assertEquals(defaultTemplate.getHardware().getId(), "m1.small");
+      assertEquals(defaultTemplate.getLocation().getId(), "is-1");
+      assertEquals(defaultTemplate.getLocation().getScope(), LocationScope.REGION);
+      assertEquals(AWSUtils.getRegionFromLocationOrNull(defaultTemplate.getLocation()), "is-1");
+      assertEquals(getCores(defaultTemplate.getHardware()), 1.0d);
+   }
+
+   @Override
+   protected Set<String> getIso3166Codes() {
+      return ImmutableSet.<String> of("IS-1");
+   }
+}
diff --git a/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/strategy/GreenQloudComputeReviseParsedImageTest.java b/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/strategy/GreenQloudComputeReviseParsedImageTest.java
new file mode 100644
index 0000000..7ad0cdf
--- /dev/null
+++ b/labs/greenqloud-compute/src/test/java/org/jclouds/greenqloud/compute/strategy/GreenQloudComputeReviseParsedImageTest.java
@@ -0,0 +1,256 @@
+/**
+ * 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.greenqloud.compute.strategy;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.compute.config.BaseComputeServiceContextModule;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule;
+import org.jclouds.ec2.compute.functions.EC2ImageParser;
+import org.jclouds.ec2.compute.strategy.EC2PopulateDefaultLoginCredentialsForImageStrategy;
+import org.jclouds.ec2.domain.Image;
+import org.jclouds.ec2.xml.DescribeImagesResponseHandlerTest;
+import org.jclouds.greenqloud.compute.strategy.GreenQloudComputeReviseParsedImage;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.inject.Guice;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "GreenQloudComputeReviseParsedImageTest")
+public class GreenQloudComputeReviseParsedImageTest {
+
+   public void testParseGreenQloudImage() {
+
+      Set<org.jclouds.compute.domain.Image> result = convertImages("/greenqloud_images.xml");
+      assertEquals(result.size(), 9);
+
+      assertEquals(
+            Iterables.get(result, 0).toString(),
+            new ImageBuilder()
+                  .operatingSystem(
+                        OperatingSystem.builder().family(OsFamily.UBUNTU).version("10.04")
+                              .description("Ubuntu Server 10.04 (64-bit)").is64Bit(true)
+                              .arch("paravirtual").build())
+                  .description("Ubuntu Server 10.04 (64-bit)")
+                  .name("Ubuntu Server 10.04.3")
+                  .defaultCredentials(new LoginCredentials("root", false))
+                  .id("us-east-1/qmi-4e5b842f")
+                  .providerId("qmi-4e5b842f")
+                  .location(defaultLocation)
+                  .userMetadata(
+                        ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                              "paravirtual", "hypervisor", "xen"))
+                  .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());
+      assertEquals(Iterables.get(result, 0).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);
+
+      assertEquals(
+            Iterables.get(result, 1).toString(),
+            new ImageBuilder()
+                  .operatingSystem(
+                        OperatingSystem.builder().family(OsFamily.UBUNTU).version("11.10")
+                              .description("Ubuntu Server 11.10 (64-bit)").is64Bit(true)
+                              .arch("paravirtual").build())
+                  .description("Ubuntu Server 11.10 (64-bit)")
+                  .name("Ubuntu Server 11.10")
+                  .defaultCredentials(new LoginCredentials("root", false))
+                  .id("us-east-1/qmi-9ac92558")
+                  .providerId("qmi-9ac92558")
+                  .location(defaultLocation)
+                  .userMetadata(
+                        ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                              "paravirtual", "hypervisor", "xen"))
+                  .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());
+      assertEquals(Iterables.get(result, 1).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);
+      
+      assertEquals(
+               Iterables.get(result, 2).toString(),
+               new ImageBuilder()
+                  .operatingSystem(
+                        OperatingSystem.builder().family(OsFamily.CENTOS).version("5.5")
+                              .description("CentOS 5.5 Server 64-bit").is64Bit(true)
+                              .arch("paravirtual").build())
+                  .description("CentOS 5.5 Server 64-bit")
+                  .name("CentOS 5.5 Server")
+                  .defaultCredentials(new LoginCredentials("root", false))
+                  .id("us-east-1/qmi-33a467aa")
+                  .providerId("qmi-33a467aa")
+                  .location(defaultLocation)
+                  .userMetadata(
+                        ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                              "paravirtual", "hypervisor", "xen"))
+                  .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());               
+         assertEquals(Iterables.get(result, 2).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);
+         
+         assertEquals(
+                  Iterables.get(result, 3).toString(),
+                  new ImageBuilder()
+                     .operatingSystem(
+                           OperatingSystem.builder().family(OsFamily.DEBIAN).version("6.0")
+                                 .description("Debian 6.0 (64-bit)").is64Bit(true)
+                                 .arch("paravirtual").build())
+                     .description("Debian 6.0 (64-bit)")
+                     .name("Debian 6.0")
+                     .defaultCredentials(new LoginCredentials("root", false))
+                     .id("us-east-1/qmi-f2a9d2ba")
+                     .providerId("qmi-f2a9d2ba")
+                     .location(defaultLocation)
+                     .userMetadata(
+                           ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                                 "paravirtual", "hypervisor", "xen"))
+                     .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());               
+         assertEquals(Iterables.get(result, 3).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);         
+
+         assertEquals(
+                  Iterables.get(result, 4).toString(),
+                  new ImageBuilder()
+                     .operatingSystem(
+                           OperatingSystem.builder().family(OsFamily.CENTOS).version("6.0")
+                                 .description("CentOS 6.0 (64-bit)").is64Bit(true)
+                                 .arch("paravirtual").build())
+                     .description("CentOS 6.0 (64-bit)")
+                     .name("CentOS 6.0")
+                     .defaultCredentials(new LoginCredentials("root", false))
+                     .id("us-east-1/qmi-96f82145")
+                     .providerId("qmi-96f82145")
+                     .location(defaultLocation)
+                     .userMetadata(
+                           ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                                 "paravirtual", "hypervisor", "xen"))
+                     .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());               
+            assertEquals(Iterables.get(result, 4).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);         
+
+            assertEquals(
+                     Iterables.get(result, 5).toString(),
+                     new ImageBuilder()
+                        .operatingSystem(
+                              OperatingSystem.builder().family(OsFamily.CENTOS).version("6.0")
+                                    .description("CentOS 6.0 GUI (64-bit)").is64Bit(true)
+                                    .arch("paravirtual").build())
+                        .description("CentOS 6.0 GUI (64-bit)")
+                        .name("CentOS 6.0 GUI")
+                        .defaultCredentials(new LoginCredentials("root", false))
+                        .id("us-east-1/qmi-42e877f6")
+                        .providerId("qmi-42e877f6")
+                        .location(defaultLocation)
+                        .userMetadata(
+                              ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                                    "paravirtual", "hypervisor", "xen"))
+                        .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());               
+            assertEquals(Iterables.get(result, 5).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);      
+
+            assertEquals(
+                     Iterables.get(result, 6).toString(),
+                     new ImageBuilder()
+                           .operatingSystem(
+                                 OperatingSystem.builder().family(OsFamily.UBUNTU).version("11.04")
+                                       .description("Ubuntu Server 11.04 (64-bit)").is64Bit(true)
+                                       .arch("paravirtual").build())
+                           .description("Ubuntu Server 11.04 (64-bit)")
+                           .name("Ubuntu Server 11.04")
+                           .defaultCredentials(new LoginCredentials("root", false))
+                           .id("us-east-1/qmi-eed8cea7")
+                           .providerId("qmi-eed8cea7")
+                           .location(defaultLocation)
+                           .userMetadata(
+                                 ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                                       "paravirtual", "hypervisor", "xen"))
+                           .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());
+            assertEquals(Iterables.get(result, 6).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);
+
+            assertEquals(
+                     Iterables.get(result, 7).toString(),
+                     new ImageBuilder()
+                        .operatingSystem(
+                              // fedora is not in the image version map, yet
+                              OperatingSystem.builder().family(OsFamily.FEDORA).version("")
+                                    .description("Fedora 16 Server").is64Bit(false)
+                                    .arch("paravirtual").build())
+                        .description("Fedora 16 Server")
+                        .name("Fedora 16 Server")
+                        .defaultCredentials(new LoginCredentials("root", false))
+                        .id("us-east-1/qmi-fa4bdae0")
+                        .providerId("qmi-fa4bdae0")
+                        .location(defaultLocation)
+                        .userMetadata(
+                              ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                                    "paravirtual", "hypervisor", "xen"))
+                        .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());               
+            assertEquals(Iterables.get(result, 7).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);      
+           
+            assertEquals(
+                     Iterables.get(result, 8).toString(),
+                     new ImageBuilder()
+                        .operatingSystem(
+                              OperatingSystem.builder().family(OsFamily.CENTOS).version("5.6")
+                                    .description("CentOS 5.6 Server GUI 64-bit").is64Bit(true)
+                                    .arch("paravirtual").build())
+                        .description("CentOS 5.6 Server GUI 64-bit")
+                        .name("CentOS 5.6 GUI")
+                        .defaultCredentials(new LoginCredentials("root", false))
+                        .id("us-east-1/qmi-93271d32")
+                        .providerId("qmi-93271d32")
+                        .location(defaultLocation)
+                        .userMetadata(
+                              ImmutableMap.of("owner", "admin", "rootDeviceType", "ebs", "virtualizationType",
+                                    "paravirtual", "hypervisor", "xen"))
+                        .status(org.jclouds.compute.domain.Image.Status.AVAILABLE).backendStatus("available").build().toString());               
+            assertEquals(Iterables.get(result, 8).getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);      
+
+   }
+
+   static Location defaultLocation = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1").description(
+            "us-east-1").build();
+
+   public static Set<org.jclouds.compute.domain.Image> convertImages(String resource) {
+
+      Map<OsFamily, Map<String, String>> map = new BaseComputeServiceContextModule() {
+      }.provideOsVersionMap(new ComputeServiceConstants.ReferenceData(), Guice.createInjector(new GsonModule())
+               .getInstance(Json.class));
+
+      // note this method is what mandates the location id as us-east-1
+      Set<Image> result = DescribeImagesResponseHandlerTest.parseImages(resource);
+      EC2ImageParser parser = new EC2ImageParser(EC2ComputeServiceDependenciesModule.toPortableImageStatus,
+               new EC2PopulateDefaultLoginCredentialsForImageStrategy(), map, Suppliers
+                        .<Set<? extends Location>> ofInstance(ImmutableSet.<Location> of(defaultLocation)), Suppliers
+                        .ofInstance(defaultLocation), new GreenQloudComputeReviseParsedImage(map));
+      return Sets.newLinkedHashSet(Iterables.filter(Iterables.transform(result, parser), Predicates.notNull()));
+   }
+
+}
diff --git a/labs/greenqloud-compute/src/test/resources/greenqloud_images.xml b/labs/greenqloud-compute/src/test/resources/greenqloud_images.xml
new file mode 100644
index 0000000..8052445
--- /dev/null
+++ b/labs/greenqloud-compute/src/test/resources/greenqloud_images.xml
@@ -0,0 +1,257 @@
+<DescribeImagesResponse xmlns="http://ec2.amazonaws.com/doc/2010-06-15/">
+    <requestId>1c016525-12ae-4fc6-8d4e-754692412bbc</requestId>
+    <imagesSet>
+        <item>
+            <imageId>qmi-4e5b842f</imageId>
+            <imageLocation>Ubuntu Server 10.04 (64-bit)</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>Ubuntu Server 10.04.3</name>
+            <description>Ubuntu Server 10.04 (64-bit)</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+        <item>
+            <imageId>qmi-9ac92558</imageId>
+            <imageLocation>Ubuntu Server 11.10 (64-bit)</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>Ubuntu Server 11.10</name>
+            <description>Ubuntu Server 11.10 (64-bit)</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+        <item>
+            <imageId>qmi-33a467aa</imageId>
+            <imageLocation>CentOS 5.5 Server 64-bit</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>CentOS 5.5 Server</name>
+            <description>CentOS 5.5 Server 64-bit</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+        <item>
+            <imageId>qmi-f2a9d2ba</imageId>
+            <imageLocation>Debian 6.0 (64-bit)</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>Debian 6.0</name>
+            <description>Debian 6.0 (64-bit)</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+        <item>
+            <imageId>qmi-96f82145</imageId>
+            <imageLocation>CentOS 6.0 (64-bit)</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>CentOS 6.0</name>
+            <description>CentOS 6.0 (64-bit)</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+        <item>
+            <imageId>qmi-42e877f6</imageId>
+            <imageLocation>CentOS 6.0 GUI (64-bit)</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>CentOS 6.0 GUI</name>
+            <description>CentOS 6.0 GUI (64-bit)</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+        <item>
+            <imageId>qmi-eed8cea7</imageId>
+            <imageLocation>Ubuntu Server 11.04 (64-bit)</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>Ubuntu Server 11.04</name>
+            <description>Ubuntu Server 11.04 (64-bit)</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+        <item>
+            <imageId>qmi-fa4bdae0</imageId>
+            <imageLocation>Fedora 16 Server</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>Fedora 16 Server</name>
+            <description>Fedora 16 Server</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+        <item>
+            <imageId>qmi-93271d32</imageId>
+            <imageLocation>CentOS 5.6 Server GUI 64-bit</imageLocation>
+            <imageState>available</imageState>
+            <imageOwnerId>admin</imageOwnerId>
+            <isPublic>true</isPublic>
+            <productCodes>
+                <item>
+                    <productCode></productCode>
+                </item>
+            </productCodes>
+            <architecture></architecture>
+            <imageType>machine</imageType>
+            <kernelId></kernelId>
+            <ramdiskId></ramdiskId>
+            <platform></platform>
+            <stateReason>
+                <code></code>
+                <message></message>
+            </stateReason>
+            <imageOwnerAlias></imageOwnerAlias>
+            <name>CentOS 5.6 GUI</name>
+            <description>CentOS 5.6 Server GUI 64-bit</description>
+            <rootDeviceType>ebs</rootDeviceType>
+            <rootDeviceName></rootDeviceName>
+            <blockDeviceMapping />
+            <hypervisor>xen</hypervisor>
+        </item>
+    </imagesSet>
+</DescribeImagesResponse>
\ No newline at end of file
diff --git a/labs/greenqloud-storage/pom.xml b/labs/greenqloud-storage/pom.xml
new file mode 100644
index 0000000..f9e9759
--- /dev/null
+++ b/labs/greenqloud-storage/pom.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.jclouds</groupId>
+        <artifactId>jclouds-project</artifactId>
+        <version>1.5.0-SNAPSHOT</version>
+        <relativePath>../../project/pom.xml</relativePath>
+    </parent>
+    <groupId>org.jclouds.labs</groupId>
+    <artifactId>greenqloud-storage</artifactId>
+    <name>jclouds GreenQloud Simple Storage Service (S3) provider</name>
+    <description>Simple Storage Service (S3) implementation targeted to GreenQloud Web Services</description>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <test.greenqloud-storage.endpoint>https://s.greenqloud.com</test.greenqloud-storage.endpoint>
+        <test.greenqloud-storage.api-version>2006-03-01</test.greenqloud-storage.api-version>
+        <test.greenqloud-storage.build-version></test.greenqloud-storage.build-version>
+        <test.greenqloud-storage.identity>${test.greenqloud.identity}</test.greenqloud-storage.identity>
+        <test.greenqloud-storage.credential>${test.greenqloud.credential}</test.greenqloud-storage.credential>
+        <jclouds.osgi.export>org.jclouds.greenqloud.storage*;version="${project.version}"</jclouds.osgi.export>
+        <jclouds.osgi.import>
+          org.jclouds*;version="${project.version}",
+          *
+        </jclouds.osgi.import>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jclouds.api</groupId>
+            <artifactId>s3</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds</groupId>
+            <artifactId>jclouds-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds</groupId>
+            <artifactId>jclouds-blobstore</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds.api</groupId>
+            <artifactId>s3</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds.driver</groupId>
+            <artifactId>jclouds-log4j</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.16</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>live</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>integration</id>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <systemPropertyVariables>
+                                        <test.greenqloud-storage.endpoint>${test.greenqloud-storage.endpoint}</test.greenqloud-storage.endpoint>
+                                        <test.greenqloud-storage.api-version>${test.greenqloud-storage.api-version}</test.greenqloud-storage.api-version>
+                                        <test.greenqloud-storage.build-version>${test.greenqloud-storage.build-version}</test.greenqloud-storage.build-version>
+                                        <test.greenqloud-storage.identity>${test.greenqloud-storage.identity}</test.greenqloud-storage.identity>
+                                        <test.greenqloud-storage.credential>${test.greenqloud-storage.credential}</test.greenqloud-storage.credential>
+                                        <jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
+                                        <jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
+                                    </systemPropertyVariables>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/labs/greenqloud-storage/src/main/java/org/jclouds/greenqloud/storage/GreenQloudStorageProviderMetadata.java b/labs/greenqloud-storage/src/main/java/org/jclouds/greenqloud/storage/GreenQloudStorageProviderMetadata.java
new file mode 100644
index 0000000..e6e9c009
--- /dev/null
+++ b/labs/greenqloud-storage/src/main/java/org/jclouds/greenqloud/storage/GreenQloudStorageProviderMetadata.java
@@ -0,0 +1,94 @@
+/**
+ * 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.greenqloud.storage;
+
+import static org.jclouds.location.reference.LocationConstants.ENDPOINT;
+import static org.jclouds.location.reference.LocationConstants.ISO3166_CODES;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+import org.jclouds.s3.S3ApiMetadata;
+
+/**
+ * Implementation of {@link org.jclouds.providers.ProviderMetadata} for GreenQloud Simple Storage Service (S3).
+ * 
+ * @author Adrian Cole
+ */
+public class GreenQloudStorageProviderMetadata extends BaseProviderMetadata {
+   
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 1L;
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   @Override
+   public Builder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
+   
+   public GreenQloudStorageProviderMetadata() {
+      super(builder());
+   }
+
+   public GreenQloudStorageProviderMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = new Properties();
+      properties.setProperty(PROPERTY_REGIONS, "is-1");
+      properties.setProperty(PROPERTY_REGION + ".is-1." + ISO3166_CODES, "IS-1");
+      properties.setProperty(PROPERTY_REGION + "." + "is-1" + "." + ENDPOINT, "https://s.greenqloud.com");
+      return properties;
+   }
+   
+   public static class Builder extends BaseProviderMetadata.Builder {
+
+      protected Builder(){
+         id("greenqloud-storage")
+         .name("GreenQloud Simple Storage Service (S3)")
+         .apiMetadata(new S3ApiMetadata())
+         .homepage(URI.create("http://www.greenqloud.com"))
+         .console(URI.create("https://manage.greenqloud.com"))
+         .linkedServices("greenqloud-compute", "greenqloud-storage")
+         .iso3166Codes("IS-1")
+         .endpoint("https://s.greenqloud.com")
+         .defaultProperties(GreenQloudStorageProviderMetadata.defaultProperties());
+      }
+
+      @Override
+      public GreenQloudStorageProviderMetadata build() {
+         return new GreenQloudStorageProviderMetadata(this);
+      }
+      
+      @Override
+      public Builder fromProviderMetadata(
+            ProviderMetadata in) {
+         super.fromProviderMetadata(in);
+         return this;
+      }
+   }
+}
\ No newline at end of file
diff --git a/labs/greenqloud-storage/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/labs/greenqloud-storage/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
new file mode 100644
index 0000000..a51135a
--- /dev/null
+++ b/labs/greenqloud-storage/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
@@ -0,0 +1 @@
+org.jclouds.greenqloud.storage.GreenQloudStorageProviderMetadata
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/GreenQloudStorageClientLiveTest.java
similarity index 63%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/GreenQloudStorageClientLiveTest.java
index 771a1be..d6329aa 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/GreenQloudStorageClientLiveTest.java
@@ -16,13 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.storage;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.s3.S3ClientLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
-
-   void startPool() throws RunNodesException;
-
+/**
+ * Tests behavior of {@code S3Client}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "GreenQloudStorageClientLiveTest")
+public class GreenQloudStorageClientLiveTest extends S3ClientLiveTest {
+   public GreenQloudStorageClientLiveTest() {
+      provider = "greenqloud-storage";
+   }
 }
diff --git a/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/GreenQloudStorageProviderTest.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/GreenQloudStorageProviderTest.java
new file mode 100644
index 0000000..93a6153
--- /dev/null
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/GreenQloudStorageProviderTest.java
@@ -0,0 +1,37 @@
+/**
+ * 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.greenqloud.storage;
+
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.jclouds.s3.S3ApiMetadata;
+import org.testng.annotations.Test;
+
+/**
+ * The GreenQloudStorageProviderTest tests the {@link GreenQloudStorageProviderMetadata} class.
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "GreenQloudStorageProviderTest")
+public class GreenQloudStorageProviderTest extends BaseProviderMetadataTest {
+
+   public GreenQloudStorageProviderTest() {
+      super(new GreenQloudStorageProviderMetadata(), new S3ApiMetadata());
+   }
+
+}
\ No newline at end of file
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobIntegrationLiveTest.java
similarity index 63%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobIntegrationLiveTest.java
index 771a1be..8a0d4bb 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobIntegrationLiveTest.java
@@ -16,13 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.storage.blobstore;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.s3.blobstore.integration.S3BlobIntegrationLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "GreenQloudStorageBlobIntegrationLiveTest")
+public class GreenQloudStorageBlobIntegrationLiveTest extends S3BlobIntegrationLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public GreenQloudStorageBlobIntegrationLiveTest() {
+      provider = "greenqloud-storage";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobLiveTest.java
similarity index 66%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobLiveTest.java
index 771a1be..6a69b15 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobLiveTest.java
@@ -16,13 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.storage.blobstore;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.s3.blobstore.integration.S3BlobLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "GreenQloudStorageBlobLiveTest")
+public class GreenQloudStorageBlobLiveTest extends S3BlobLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public GreenQloudStorageBlobLiveTest() {
+      provider = "greenqloud-storage";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobMapIntegrationLiveTest.java
similarity index 62%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobMapIntegrationLiveTest.java
index 771a1be..d405dee 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobMapIntegrationLiveTest.java
@@ -16,13 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.storage.blobstore;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.s3.blobstore.integration.S3BlobMapIntegrationLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "GreenQloudStorageBlobMapIntegrationLiveTest")
+public class GreenQloudStorageBlobMapIntegrationLiveTest extends S3BlobMapIntegrationLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public GreenQloudStorageBlobMapIntegrationLiveTest() {
+      provider = "greenqloud-storage";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobSignerLiveTest.java
similarity index 64%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobSignerLiveTest.java
index 771a1be..32cbba3 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageBlobSignerLiveTest.java
@@ -16,13 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.storage.blobstore;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.s3.blobstore.integration.S3BlobSignerLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "GreenQloudStorageBlobSignerLiveTest")
+public class GreenQloudStorageBlobSignerLiveTest extends S3BlobSignerLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public GreenQloudStorageBlobSignerLiveTest() {
+      provider = "greenqloud-storage";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageContainerIntegrationLiveTest.java
similarity index 61%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageContainerIntegrationLiveTest.java
index 771a1be..563a27e 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageContainerIntegrationLiveTest.java
@@ -16,13 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.storage.blobstore;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.s3.blobstore.integration.S3ContainerIntegrationLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "GreenQloudStorageContainerIntegrationLiveTest")
+public class GreenQloudStorageContainerIntegrationLiveTest extends S3ContainerIntegrationLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public GreenQloudStorageContainerIntegrationLiveTest() {
+      provider = "greenqloud-storage";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageContainerLiveTest.java
similarity index 64%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageContainerLiveTest.java
index 771a1be..4453084 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageContainerLiveTest.java
@@ -16,13 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.storage.blobstore;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * @author Adrian Cole
+ */
+@Test(groups =  "live", testName = "GreenQloudStorageContainerLiveTest")
+public class GreenQloudStorageContainerLiveTest extends S3ContainerLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public GreenQloudStorageContainerLiveTest() {
+      provider = "greenqloud-storage";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageInputStreamMapIntegrationLiveTest.java
similarity index 60%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageInputStreamMapIntegrationLiveTest.java
index 771a1be..611b739 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageInputStreamMapIntegrationLiveTest.java
@@ -16,13 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.greenqloud.storage.blobstore;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.s3.blobstore.integration.S3InputStreamMapIntegrationLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "GreenQloudStorageInputStreamMapIntegrationLiveTest")
+public class GreenQloudStorageInputStreamMapIntegrationLiveTest extends
+      S3InputStreamMapIntegrationLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public GreenQloudStorageInputStreamMapIntegrationLiveTest() {
+      provider = "greenqloud-storage";
+   }
 }
diff --git a/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageServiceIntegrationLiveTest.java b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageServiceIntegrationLiveTest.java
new file mode 100644
index 0000000..7ea1a5f
--- /dev/null
+++ b/labs/greenqloud-storage/src/test/java/org/jclouds/greenqloud/storage/blobstore/GreenQloudStorageServiceIntegrationLiveTest.java
@@ -0,0 +1,43 @@
+/**
+ * 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.greenqloud.storage.blobstore;
+
+import java.util.Set;
+
+import org.jclouds.s3.blobstore.integration.S3ServiceIntegrationLiveTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "GreenQloudStorageServiceIntegrationLiveTest")
+public class GreenQloudStorageServiceIntegrationLiveTest extends S3ServiceIntegrationLiveTest {
+
+   public GreenQloudStorageServiceIntegrationLiveTest() {
+      provider = "greenqloud-storage";
+   }
+
+   @Override
+   protected Set<String> getIso3166Codes() {
+      return ImmutableSet.<String> of("IS-1");
+   }
+
+}
diff --git a/labs/joyent-sdc/pom.xml b/labs/joyent-sdc/pom.xml
index 4dd22ef..e307093 100644
--- a/labs/joyent-sdc/pom.xml
+++ b/labs/joyent-sdc/pom.xml
@@ -50,11 +50,18 @@
   <dependencies>
     <dependency>
       <groupId>org.jclouds</groupId>
-      <artifactId>jclouds-core</artifactId>
+      <artifactId>jclouds-compute</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>org.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds</groupId>
       <artifactId>jclouds-core</artifactId>
       <version>${project.version}</version>
       <type>test-jar</type>
@@ -67,6 +74,12 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.jclouds.driver</groupId>
+      <artifactId>jclouds-sshj</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-classic</artifactId>
       <version>1.0.0</version>
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java
index 3182b60..e9679e8 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCApiMetadata.java
@@ -22,6 +22,10 @@
 import java.util.Properties;
 
 import org.jclouds.apis.ApiMetadata;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.joyent.sdc.v6_5.compute.config.SDCComputeServiceContextModule;
+import org.jclouds.joyent.sdc.v6_5.config.DatacentersAreZonesModule;
+import org.jclouds.joyent.sdc.v6_5.config.SDCProperties;
 import org.jclouds.joyent.sdc.v6_5.config.SDCRestClientModule;
 import org.jclouds.rest.RestContext;
 import org.jclouds.rest.internal.BaseRestApiMetadata;
@@ -59,6 +63,7 @@
 
    public static Properties defaultProperties() {
       Properties properties = BaseRestApiMetadata.defaultProperties();
+      properties.setProperty(SDCProperties.AUTOGENERATE_KEYS, "true");
       return properties;
    }
 
@@ -66,10 +71,16 @@
 
       protected Builder() {
          super(SDCClient.class, SDCAsyncClient.class);
-         id("joyent-sdc").name("Joyent SDC API").identityName("username").credentialName("password")
-               .documentation(URI.create("http://sdc.joyent.org/sdcapi.html")).version("~6.5")
-               .defaultEndpoint("https://api.joyentcloud.com").defaultProperties(SDCApiMetadata.defaultProperties())
-               .defaultModules(ImmutableSet.<Class<? extends Module>> of(SDCRestClientModule.class));
+         id("joyent-sdc")
+         .name("Joyent SDC API")
+         .identityName("username")
+         .credentialName("password")
+         .documentation(URI.create("http://sdc.joyent.org/sdcapi.html"))
+         .version("~6.5")
+         .defaultEndpoint("https://api.joyentcloud.com")
+         .defaultProperties(SDCApiMetadata.defaultProperties())
+         .view(TypeToken.of(ComputeServiceContext.class))
+         .defaultModules(ImmutableSet.<Class<? extends Module>> of(DatacentersAreZonesModule.class, SDCRestClientModule.class, SDCComputeServiceContextModule.class));
       }
 
       @Override
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java
index 7a15079..f6b24b6 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCAsyncClient.java
@@ -18,11 +18,20 @@
  */
 package org.jclouds.joyent.sdc.v6_5;
 
+import java.util.Set;
+
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.joyent.sdc.v6_5.features.DatacenterAsyncClient;
 import org.jclouds.joyent.sdc.v6_5.features.DatasetAsyncClient;
+import org.jclouds.joyent.sdc.v6_5.features.KeyAsyncClient;
 import org.jclouds.joyent.sdc.v6_5.features.MachineAsyncClient;
 import org.jclouds.joyent.sdc.v6_5.features.PackageAsyncClient;
+import org.jclouds.location.Zone;
+import org.jclouds.location.functions.ZoneToEndpoint;
 import org.jclouds.rest.annotations.Delegate;
+import org.jclouds.rest.annotations.EndpointParam;
+
+import com.google.inject.Provides;
 
 /**
  * Provides asynchronous access to SDC via their REST API.
@@ -33,7 +42,15 @@
  * @author Adrian Cole
  */
 public interface SDCAsyncClient {
-
+   
+   /**
+    * 
+    * @return the datacenter codes configured
+    */
+   @Provides
+   @Zone
+   Set<String> getConfiguredDatacenters();
+   
    /**
     * Provides asynchronous access to Datacenter features.
     */
@@ -41,20 +58,29 @@
    DatacenterAsyncClient getDatacenterClient();
 
    /**
+    * Provides asynchronous access to Key features.
+    */
+   @Delegate
+   KeyAsyncClient getKeyClient();
+
+   /**
     * Provides asynchronous access to Machine features.
     */
    @Delegate
-   MachineAsyncClient getMachineClient();
+   MachineAsyncClient getMachineClientForDatacenter(
+         @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String datacenter);
 
    /**
     * Provides asynchronous access to Dataset features.
     */
    @Delegate
-   DatasetAsyncClient getDatasetClient();
+   DatasetAsyncClient getDatasetClientForDatacenter(
+         @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String datacenter);
 
    /**
     * Provides asynchronous access to Package features.
     */
    @Delegate
-   PackageAsyncClient getPackageClient();
+   PackageAsyncClient getPackageClientForDatacenter(
+         @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String datacenter);
 }
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java
index e0488fd..fca489b 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/SDCClient.java
@@ -18,14 +18,22 @@
  */
 package org.jclouds.joyent.sdc.v6_5;
 
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import org.jclouds.concurrent.Timeout;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.joyent.sdc.v6_5.features.DatacenterClient;
 import org.jclouds.joyent.sdc.v6_5.features.DatasetClient;
+import org.jclouds.joyent.sdc.v6_5.features.KeyClient;
 import org.jclouds.joyent.sdc.v6_5.features.MachineClient;
 import org.jclouds.joyent.sdc.v6_5.features.PackageClient;
+import org.jclouds.location.Zone;
+import org.jclouds.location.functions.ZoneToEndpoint;
 import org.jclouds.rest.annotations.Delegate;
+import org.jclouds.rest.annotations.EndpointParam;
+
+import com.google.inject.Provides;
 
 /**
  * Provides synchronous access to SDC.
@@ -39,26 +47,43 @@
 public interface SDCClient {
 
    /**
+    * 
+    * @return the datacenter codes configured
+    */
+   @Provides
+   @Zone
+   Set<String> getConfiguredDatacenters();
+
+   /**
     * Provides synchronous access to Datacenter features.
     */
    @Delegate
    DatacenterClient getDatacenterClient();
-
+   
+   /**
+    * Provides synchronous access to Key features.
+    */
+   @Delegate
+   KeyClient getKeyClient();
+   
    /**
     * Provides synchronous access to Machine features.
     */
    @Delegate
-   MachineClient getMachineClient();
+   MachineClient getMachineClientForDatacenter(
+         @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String datacenter);
 
    /**
     * Provides synchronous access to Dataset features.
     */
    @Delegate
-   DatasetClient getDatasetClient();
+   DatasetClient getDatasetClientForDatacenter(
+         @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String datacenter);
 
    /**
     * Provides synchronous access to Package features.
     */
    @Delegate
-   PackageClient getPackageClient();
+   PackageClient getPackageClientForDatacenter(
+         @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String datacenter);
 }
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/binders/BindKeyToJsonPayload.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/binders/BindKeyToJsonPayload.java
new file mode 100644
index 0000000..15dc5e6
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/binders/BindKeyToJsonPayload.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.joyent.sdc.v6_5.binders;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+import org.jclouds.json.Json;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+/**
+ * 
+ * @author Adrian Cole
+ * 
+ */
+@Singleton
+public class BindKeyToJsonPayload extends BindToJsonPayload {
+   @Inject
+   public BindKeyToJsonPayload(Json jsonBinder) {
+      super(jsonBinder);
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object toBind) {
+      // don't include created in the http request
+      return super.bindToRequest(request, Key.class.cast(toBind).toBuilder().created(null).build());
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
+      throw new IllegalStateException("This should be assigned only a single arg");
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeService.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeService.java
new file mode 100644
index 0000000..e6e4cc9
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeService.java
@@ -0,0 +1,153 @@
+/**
+ * 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.joyent.sdc.v6_5.compute;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.internal.BaseComputeService;
+import org.jclouds.compute.internal.PersistNodeCredentials;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
+import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
+import org.jclouds.compute.strategy.DestroyNodeStrategy;
+import org.jclouds.compute.strategy.GetImageStrategy;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.RebootNodeStrategy;
+import org.jclouds.compute.strategy.ResumeNodeStrategy;
+import org.jclouds.compute.strategy.SuspendNodeStrategy;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import org.jclouds.joyent.sdc.v6_5.SDCClient;
+import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey;
+import org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+import org.jclouds.joyent.sdc.v6_5.features.KeyClient;
+import org.jclouds.joyent.sdc.v6_5.predicates.KeyPredicates;
+import org.jclouds.scriptbuilder.functions.InitAdminAccess;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class SDCComputeService extends BaseComputeService {
+   protected final SDCClient novaClient;
+   protected final LoadingCache<DatacenterAndName, KeyAndPrivateKey> keyCache;
+   protected final Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByDatacenterId;
+   protected final GroupNamingConvention.Factory namingConvention;
+
+   @Inject
+   protected SDCComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
+         @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
+         @Memoized Supplier<Set<? extends Location>> locations, ListNodesStrategy listNodesStrategy,
+         GetImageStrategy getImageStrategy, GetNodeMetadataStrategy getNodeMetadataStrategy,
+         CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy,
+         DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy startNodeStrategy,
+         SuspendNodeStrategy stopNodeStrategy, Provider<TemplateBuilder> templateBuilderProvider,
+         Provider<TemplateOptions> templateOptionsProvider,
+         @Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning,
+         @Named(TIMEOUT_NODE_TERMINATED) Predicate<AtomicReference<NodeMetadata>> nodeTerminated,
+         @Named(TIMEOUT_NODE_SUSPENDED) Predicate<AtomicReference<NodeMetadata>> nodeSuspended,
+         InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory,
+         RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
+         PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
+         @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, SDCClient novaClient,
+         LoadingCache<DatacenterAndName, KeyAndPrivateKey> keyCache,
+         Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByDatacenterId,
+         GroupNamingConvention.Factory namingConvention, Optional<ImageExtension> imageExtension) {
+      super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
+            getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
+            startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
+            nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
+            persistNodeCredentials, timeouts, executor, imageExtension);
+      this.novaClient = checkNotNull(novaClient, "novaClient");
+      this.keyCache = checkNotNull(keyCache, "keyCache");
+      this.orphanedGroupsByDatacenterId = checkNotNull(orphanedGroupsByDatacenterId, "orphanedGroupsByDatacenterId");
+      this.namingConvention = checkNotNull(namingConvention, "namingConvention");
+   }
+
+   @Override
+   protected void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends NodeMetadata> deadNodes) {
+      Multimap<String, String> zoneToZoneAndGroupNames = orphanedGroupsByDatacenterId.apply(deadNodes);
+      for (String datacenterId : zoneToZoneAndGroupNames.keySet()) {
+         cleanupOrphanedKeysInZone(ImmutableSet.copyOf(zoneToZoneAndGroupNames.get(datacenterId)), datacenterId);
+      }
+   }
+
+   private void cleanupOrphanedKeysInZone(Set<String> groups, String datacenterId) {
+      KeyClient keyClient = novaClient.getKeyClient();
+      for (String group : groups) {
+         for (Key key : Iterables.filter(keyClient.list(),
+               KeyPredicates.nameMatches(namingConvention.create().containsGroup(group)))) {
+            DatacenterAndName datacenterAndName = DatacenterAndName.fromDatacenterAndName(datacenterId, key.getName());
+            logger.debug(">> deleting key(%s)", datacenterAndName);
+            keyClient.delete(key.getName());
+            // TODO: test this clear happens
+            keyCache.invalidate(datacenterAndName);
+            logger.debug("<< deleted key(%s)", datacenterAndName);
+         }
+
+         keyCache.invalidate(DatacenterAndName.fromDatacenterAndName(datacenterId, namingConvention.create()
+               .sharedNameForGroup(group)));
+      }
+   }
+
+   /**
+    * returns template options, except of type {@link SDCTemplateOptions}.
+    */
+   @Override
+   public SDCTemplateOptions templateOptions() {
+      return SDCTemplateOptions.class.cast(super.templateOptions());
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceAdapter.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceAdapter.java
new file mode 100644
index 0000000..97ce63f
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceAdapter.java
@@ -0,0 +1,203 @@
+/**
+ * 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.joyent.sdc.v6_5.compute;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.transform;
+import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsCommaDelimitedValue;
+
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.joyent.sdc.v6_5.SDCClient;
+import org.jclouds.joyent.sdc.v6_5.domain.Dataset;
+import org.jclouds.joyent.sdc.v6_5.domain.Machine;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndId;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatasetInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.MachineInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.PackageInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.options.CreateMachineOptions;
+import org.jclouds.location.Zone;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+
+/**
+ * The adapter used by the SDCComputeServiceContextModule to interface the
+ * SDC-specific domain model to the computeService generic domain model.
+ * 
+ * @author Adrian Cole
+ */
+public class SDCComputeServiceAdapter implements
+      ComputeServiceAdapter<MachineInDatacenter, PackageInDatacenter, DatasetInDatacenter, Location> {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   protected final SDCClient sdcClient;
+   protected final Supplier<Set<String>> datacenterIds;
+
+   @Inject
+   public SDCComputeServiceAdapter(SDCClient sdcClient, @Zone Supplier<Set<String>> datacenterIds) {
+      this.sdcClient = checkNotNull(sdcClient, "sdcClient");
+      this.datacenterIds = checkNotNull(datacenterIds, "datacenterIds");
+   }
+
+   @Override
+   public NodeAndInitialCredentials<MachineInDatacenter> createNodeWithGroupEncodedIntoName(String group, String name,
+         Template template) {
+
+      LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder();
+
+      CreateMachineOptions options = new CreateMachineOptions();
+      options.name(name);
+      options.packageName(template.getHardware().getProviderId());
+      options.metadata(metadataAndTagsAsCommaDelimitedValue(template.getOptions()));
+
+      String datacenterId = template.getLocation().getId();
+      String datasetURN = template.getImage().getProviderId();
+
+      logger.debug(">> creating new machine datacenter(%s) datasetURN(%s) options(%s)", datacenterId, datasetURN,
+            options);
+      Machine machine = sdcClient.getMachineClientForDatacenter(datacenterId).createWithDataset(datasetURN, options);
+
+      logger.trace("<< machine(%s)", machine.getId());
+
+      MachineInDatacenter machineInDatacenter = new MachineInDatacenter(machine, datacenterId);
+
+      //TODO machineInDatacenter.metadata for password
+      if (template.getOptions().getLoginPrivateKey() != null){
+         credentialsBuilder.privateKey(template.getOptions().getLoginPrivateKey());
+      }
+      return new NodeAndInitialCredentials<MachineInDatacenter>(machineInDatacenter, machineInDatacenter.slashEncode(),
+            credentialsBuilder.build());
+   }
+
+   @Override
+   public Iterable<PackageInDatacenter> listHardwareProfiles() {
+      Builder<PackageInDatacenter> builder = ImmutableSet.builder();
+      for (final String datacenterId : datacenterIds.get()) {
+         builder.addAll(transform(sdcClient.getPackageClientForDatacenter(datacenterId).list(),
+               new Function<org.jclouds.joyent.sdc.v6_5.domain.Package, PackageInDatacenter>() {
+
+                  @Override
+                  public PackageInDatacenter apply(org.jclouds.joyent.sdc.v6_5.domain.Package arg0) {
+                     return new PackageInDatacenter(arg0, datacenterId);
+                  }
+
+               }));
+      }
+      return builder.build();
+   }
+
+   @Override
+   public Iterable<DatasetInDatacenter> listImages() {
+      Builder<DatasetInDatacenter> builder = ImmutableSet.builder();
+      for (final String datacenterId : datacenterIds.get()) {
+         builder.addAll(transform(sdcClient.getDatasetClientForDatacenter(datacenterId).list(),
+               new Function<Dataset, DatasetInDatacenter>() {
+
+                  @Override
+                  public DatasetInDatacenter apply(Dataset arg0) {
+                     return new DatasetInDatacenter(arg0, datacenterId);
+                  }
+
+               }));
+      }
+      return builder.build();
+   }
+
+   @Override
+   public Iterable<MachineInDatacenter> listNodes() {
+      Builder<MachineInDatacenter> builder = ImmutableSet.builder();
+      for (final String datacenterId : datacenterIds.get()) {
+         builder.addAll(transform(sdcClient.getMachineClientForDatacenter(datacenterId).list(),
+               new Function<Machine, MachineInDatacenter>() {
+
+                  @Override
+                  public MachineInDatacenter apply(Machine arg0) {
+                     return new MachineInDatacenter(arg0, datacenterId);
+                  }
+
+               }));
+      }
+      return builder.build();
+   }
+
+   @Override
+   public Iterable<Location> listLocations() {
+      // locations provided by guice
+      return ImmutableSet.of();
+   }
+
+   @Override
+   public MachineInDatacenter getNode(String id) {
+      DatacenterAndId datacenterAndId = DatacenterAndId.fromSlashEncoded(id);
+      Machine machine = sdcClient.getMachineClientForDatacenter(datacenterAndId.getDatacenter()).get(
+            datacenterAndId.getId());
+      return machine == null ? null : new MachineInDatacenter(machine, datacenterAndId.getDatacenter());
+   }
+
+   @Override
+   public DatasetInDatacenter getImage(String id) {
+      DatacenterAndId datacenterAndId = DatacenterAndId.fromSlashEncoded(id);
+      Dataset dataset = sdcClient.getDatasetClientForDatacenter(datacenterAndId.getDatacenter()).get(
+            datacenterAndId.getId());
+      return dataset == null ? null : new DatasetInDatacenter(dataset, datacenterAndId.getDatacenter());
+   }
+
+   @Override
+   public void destroyNode(String id) {
+      DatacenterAndId datacenterAndId = DatacenterAndId.fromSlashEncoded(id);
+      sdcClient.getMachineClientForDatacenter(datacenterAndId.getDatacenter()).delete(datacenterAndId.getId());
+   }
+
+   @Override
+   public void rebootNode(String id) {
+      DatacenterAndId datacenterAndId = DatacenterAndId.fromSlashEncoded(id);
+      sdcClient.getMachineClientForDatacenter(datacenterAndId.getDatacenter()).reboot(datacenterAndId.getId());
+   }
+
+   @Override
+   public void resumeNode(String id) {
+      DatacenterAndId datacenterAndId = DatacenterAndId.fromSlashEncoded(id);
+      sdcClient.getMachineClientForDatacenter(datacenterAndId.getDatacenter()).stop(datacenterAndId.getId());
+
+   }
+
+   @Override
+   public void suspendNode(String id) {
+      DatacenterAndId datacenterAndId = DatacenterAndId.fromSlashEncoded(id);
+      sdcClient.getMachineClientForDatacenter(datacenterAndId.getDatacenter()).start(datacenterAndId.getId());
+
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java
new file mode 100644
index 0000000..b8c5a29
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java
@@ -0,0 +1,177 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.config;
+
+import static org.jclouds.joyent.sdc.v6_5.config.SDCProperties.AUTOGENERATE_KEYS;
+
+import java.security.SecureRandom;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Singleton;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
+import org.jclouds.domain.Location;
+import org.jclouds.functions.IdentityFunction;
+import org.jclouds.joyent.sdc.v6_5.compute.SDCComputeService;
+import org.jclouds.joyent.sdc.v6_5.compute.SDCComputeServiceAdapter;
+import org.jclouds.joyent.sdc.v6_5.compute.functions.DatasetInDatacenterToImage;
+import org.jclouds.joyent.sdc.v6_5.compute.functions.DatasetToOperatingSystem;
+import org.jclouds.joyent.sdc.v6_5.compute.functions.MachineInDatacenterToNodeMetadata;
+import org.jclouds.joyent.sdc.v6_5.compute.functions.OrphanedGroupsByDatacenterId;
+import org.jclouds.joyent.sdc.v6_5.compute.functions.PackageInDatacenterToHardware;
+import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey;
+import org.jclouds.joyent.sdc.v6_5.compute.loaders.CreateUniqueKey;
+import org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions;
+import org.jclouds.joyent.sdc.v6_5.compute.strategy.ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
+import org.jclouds.joyent.sdc.v6_5.domain.Dataset;
+import org.jclouds.joyent.sdc.v6_5.domain.Machine;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatasetInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.MachineInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.PackageInDatacenter;
+import org.jclouds.util.Iterables2;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.inject.Injector;
+import com.google.inject.Provides;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+
+/**
+ * Module for building a compute service context for SDC
+ * 
+ * @author Adrian Cole
+ */
+public class SDCComputeServiceContextModule extends
+      ComputeServiceAdapterContextModule<MachineInDatacenter, PackageInDatacenter, DatasetInDatacenter, Location> {
+
+   @SuppressWarnings("unchecked")
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(
+            new TypeLiteral<ComputeServiceAdapter<MachineInDatacenter, PackageInDatacenter, DatasetInDatacenter, Location>>() {
+            }).to(SDCComputeServiceAdapter.class);
+
+      bind(new TypeLiteral<Function<MachineInDatacenter, NodeMetadata>>() {
+      }).to(MachineInDatacenterToNodeMetadata.class);
+
+      bind(new TypeLiteral<Function<DatasetInDatacenter, Image>>() {
+      }).to(DatasetInDatacenterToImage.class);
+      bind(new TypeLiteral<Function<Dataset, OperatingSystem>>() {
+      }).to(DatasetToOperatingSystem.class);
+
+      bind(new TypeLiteral<Function<PackageInDatacenter, Hardware>>() {
+      }).to(PackageInDatacenterToHardware.class);
+
+      // we aren't converting location from a provider-specific type
+      bind(new TypeLiteral<Function<Location, Location>>() {
+      }).to(Class.class.cast(IdentityFunction.class));
+
+      // how to figure out if a group in a datacenter is no longer in use
+      bind(new TypeLiteral<Function<Set<? extends NodeMetadata>, Multimap<String, String>>>() {
+      }).to(OrphanedGroupsByDatacenterId.class);
+
+      bind(ComputeService.class).to(SDCComputeService.class);
+      bind(TemplateOptions.class).to(SDCTemplateOptions.class);
+
+      bind(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to(
+      ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.class);
+      
+      bind(new TypeLiteral<CacheLoader<DatacenterAndName, KeyAndPrivateKey>>() {
+      }).to(CreateUniqueKey.class);
+   }
+   
+   @Override
+   protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) {
+      return options.as(SDCTemplateOptions.class)
+                    .generateKey(injector.getInstance(
+                          com.google.inject.Key.get(boolean.class, Names.named(AUTOGENERATE_KEYS))));
+   }
+
+   @Provides
+   @Singleton
+   protected LoadingCache<DatacenterAndName, KeyAndPrivateKey> keyMap(
+         CacheLoader<DatacenterAndName, KeyAndPrivateKey> in) {
+      return CacheBuilder.newBuilder().build(in);
+   }
+   @Provides
+   @Singleton
+   protected Supplier<Map<String, Location>> createLocationIndexedById(
+         @Memoized Supplier<Set<? extends Location>> locations) {
+      return Suppliers.compose(new Function<Set<? extends Location>, Map<String, Location>>() {
+
+         @Override
+         public Map<String, Location> apply(Set<? extends Location> arg0) {
+            return Maps.uniqueIndex(Iterables2.concreteCopy(arg0), new Function<Location, String>() {
+
+               @Override
+               public String apply(Location arg0) {
+                  return arg0.getId();
+               }
+
+            });
+         }
+      }, locations);
+
+   }
+   
+   @Provides
+   @Singleton
+   protected SecureRandom provideSecureRandom() {
+      return new SecureRandom();
+   }
+
+   @VisibleForTesting
+   public static final Map<Machine.State, NodeMetadata.Status> toPortableNodeStatus = ImmutableMap
+         .<Machine.State, NodeMetadata.Status> builder()
+         .put(Machine.State.PROVISIONING, NodeMetadata.Status.PENDING)
+         .put(Machine.State.RUNNING, NodeMetadata.Status.RUNNING)
+         .put(Machine.State.STOPPING, NodeMetadata.Status.PENDING)
+         .put(Machine.State.OFFLINE, NodeMetadata.Status.PENDING)
+         .put(Machine.State.STOPPED, NodeMetadata.Status.SUSPENDED)
+         .put(Machine.State.DELETED, NodeMetadata.Status.TERMINATED)
+         .put(Machine.State.UNRECOGNIZED, NodeMetadata.Status.UNRECOGNIZED).build();
+
+   @Singleton
+   @Provides
+   protected Map<Machine.State, NodeMetadata.Status> toPortableNodeStatus() {
+      return toPortableNodeStatus;
+   }
+
+}
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImage.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImage.java
new file mode 100644
index 0000000..87a4053
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImage.java
@@ -0,0 +1,70 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.domain.Location;
+import org.jclouds.joyent.sdc.v6_5.domain.Dataset;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatasetInDatacenter;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+
+/**
+ * A function for transforming a sdc-specific Image into a generic Image object.
+ * 
+ * @author Adrian Cole
+ */
+public class DatasetInDatacenterToImage implements Function<DatasetInDatacenter, Image> {
+   private final Function<Dataset, OperatingSystem> imageToOs;
+   private final Supplier<Map<String, Location>> locationIndex;
+
+   @Inject
+   public DatasetInDatacenterToImage(Function<Dataset, OperatingSystem> imageToOs,
+         Supplier<Map<String, Location>> locationIndex) {
+      this.imageToOs = checkNotNull(imageToOs, "imageToOs");
+      this.locationIndex = checkNotNull(locationIndex, "locationIndex");
+   }
+
+   @Override
+   public Image apply(DatasetInDatacenter datasetInDatacenter) {
+      Location location = locationIndex.get().get(datasetInDatacenter.getDatacenter());
+      checkState(location != null, "location %s not in locationIndex: %s", datasetInDatacenter.getDatacenter(),
+            locationIndex.get());
+      Dataset dataset = datasetInDatacenter.get();
+      return new ImageBuilder()
+            .id(datasetInDatacenter.slashEncode())
+            .providerId(dataset.getId())
+            .name(dataset.getName())
+            .operatingSystem(imageToOs.apply(dataset))
+            .description(dataset.getUrn())
+            .version(dataset.getVersion())
+            .location(location)
+            .status(Image.Status.AVAILABLE).build();
+   }
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetToOperatingSystem.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetToOperatingSystem.java
new file mode 100644
index 0000000..e195f7c
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetToOperatingSystem.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.joyent.sdc.v6_5.compute.functions;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OperatingSystem.Builder;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.util.ComputeServiceUtils;
+import org.jclouds.joyent.sdc.v6_5.domain.Dataset;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A function for transforming a sdc specific Dataset into a generic
+ * OperatingSystem object.
+ * 
+ * @author Adrian Cole
+ */
+public class DatasetToOperatingSystem implements Function<Dataset, OperatingSystem> {
+   public static final Pattern DEFAULT_PATTERN = Pattern.compile("(([^ ]*) ([0-9.]+) ?.*)");
+   // Windows Machine 2008 R2 x64
+   public static final Pattern WINDOWS_PATTERN = Pattern.compile("Windows (.*) (x[86][64])");
+
+   @javax.annotation.Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final Map<OsFamily, Map<String, String>> osVersionMap;
+
+   @Inject
+   public DatasetToOperatingSystem(Map<OsFamily, Map<String, String>> osVersionMap) {
+      this.osVersionMap = osVersionMap;
+   }
+
+   public OperatingSystem apply(Dataset from) {
+      Builder builder = OperatingSystem.builder();
+      builder.name(from.getName());
+      builder.description(from.getUrn());
+      builder.is64Bit(true);//TODO: verify
+
+      List<String> pieces = ImmutableList.copyOf(Splitter.on(':').split(from.getUrn()));
+      if (pieces.get(2).indexOf('-') != -1) {
+         List<String> osFamVersion = ImmutableList.copyOf(Splitter.on('-').split(pieces.get(2)));
+         OsFamily family = OsFamily.fromValue(osFamVersion.get(0));
+         builder.family(family);
+         if (family != OsFamily.UNRECOGNIZED)
+            builder.version(ComputeServiceUtils.parseVersionOrReturnEmptyString(family, osFamVersion.get(1),
+                  osVersionMap));
+      } else {
+         builder.family(OsFamily.fromValue(pieces.get(2)));
+      }
+      return builder.build();
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadata.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadata.java
new file mode 100644
index 0000000..d7aedde
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadata.java
@@ -0,0 +1,150 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.functions;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.tryFind;
+import static com.google.common.collect.Maps.filterKeys;
+import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromCommaDelimitedValue;
+import static org.jclouds.compute.util.ComputeServiceUtils.getSpace;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.ComputeMetadata;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Location;
+import org.jclouds.joyent.sdc.v6_5.domain.Machine;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndId;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.MachineInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.reference.MetadataKeys;
+import org.jclouds.logging.Logger;
+import org.jclouds.util.InetAddresses2;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+
+/**
+ * A function for transforming a sdc-specific Machine into a generic
+ * NodeMetadata object.
+ * 
+ * @author Adrian Cole
+ */
+public class MachineInDatacenterToNodeMetadata implements Function<MachineInDatacenter, NodeMetadata> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   protected Map<Machine.State, org.jclouds.compute.domain.NodeMetadata.Status> toPortableNodeStatus;
+   protected final Supplier<Map<String, Location>> locationIndex;
+   protected final Supplier<Set<? extends Image>> images;
+   protected final Supplier<Set<? extends Hardware>> hardwares;
+   protected final GroupNamingConvention nodeNamingConvention;
+
+   @Inject
+   public MachineInDatacenterToNodeMetadata(Map<Machine.State, NodeMetadata.Status> toPortableNodeStatus,
+         Supplier<Map<String, Location>> locationIndex, @Memoized Supplier<Set<? extends Image>> images,
+         @Memoized Supplier<Set<? extends Hardware>> hardwares, GroupNamingConvention.Factory namingConvention) {
+      this.toPortableNodeStatus = checkNotNull(toPortableNodeStatus, "toPortableNodeStatus");
+      this.nodeNamingConvention = checkNotNull(namingConvention, "namingConvention").createWithoutPrefix();
+      this.locationIndex = checkNotNull(locationIndex, "locationIndex");
+      this.images = checkNotNull(images, "images");
+      this.hardwares = checkNotNull(hardwares, "hardwares");
+   }
+
+   @Override
+   public NodeMetadata apply(MachineInDatacenter machineInDatacenter) {
+      Location zone = locationIndex.get().get(machineInDatacenter.getDatacenter());
+      checkState(zone != null, "location %s not in locationIndex: %s", machineInDatacenter.getDatacenter(),
+            locationIndex.get());
+      Machine from = machineInDatacenter.get();
+
+      NodeMetadataBuilder builder = new NodeMetadataBuilder();
+      builder.id(machineInDatacenter.slashEncode());
+      builder.providerId(from.getId());
+      builder.name(from.getName());
+      builder.hostname(from.getName());
+      builder.location(zone);
+      addMetadataAndParseTagsFromCommaDelimitedValue(builder, filterKeys(from.getMetadata(), new Predicate<String>() {
+
+         @Override
+         public boolean apply(String input) {
+            // TODO make this more efficient
+            for (MetadataKeys key : MetadataKeys.values())
+               if (key.key().equals(input))
+                  return false;
+            return true;
+         }
+
+      }));
+      builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getName()));
+      builder.imageId(DatacenterAndName.fromDatacenterAndName(machineInDatacenter.getDatacenter(), from.get())
+            .slashEncode());
+      builder.operatingSystem(findOperatingSystemForMachineOrNull(machineInDatacenter));
+      builder.hardware(findHardwareForMachineOrNull(machineInDatacenter));
+      builder.status(toPortableNodeStatus.get(from.getState()));
+      builder.publicAddresses(filter(from.getIps(), not(InetAddresses2.IsPrivateIPAddress.INSTANCE)));
+      builder.privateAddresses(filter(from.getIps(), InetAddresses2.IsPrivateIPAddress.INSTANCE));
+      return builder.build();
+   }
+
+   protected Hardware findHardwareForMachineOrNull(final MachineInDatacenter machineInDatacenter) {
+      return tryFind(hardwares.get(), new Predicate<Hardware>() {
+         @Override
+         public boolean apply(Hardware input) {
+            return input.getRam() == machineInDatacenter.get().getMemorySizeMb()
+                  && getSpace(input) == machineInDatacenter.get().getDiskSizeGb()
+                  && input.getLocation().getId().equals(machineInDatacenter.getDatacenter());
+         }
+      }).orNull();
+   }
+
+   protected OperatingSystem findOperatingSystemForMachineOrNull(MachineInDatacenter machineInDatacenter) {
+      Image image = findObjectOfTypeForMachineOrNull(images.get(), "image", machineInDatacenter.get()
+            .get(), machineInDatacenter);
+      return (image != null) ? image.getOperatingSystem() : null;
+   }
+
+   public <T extends ComputeMetadata> T findObjectOfTypeForMachineOrNull(Set<? extends T> supply, String type,
+         final String objectId, final DatacenterAndId machineInDatacenter) {
+      return tryFind(supply, new Predicate<T>() {
+         @Override
+         public boolean apply(T input) {
+            return input.getId().equals(
+                  DatacenterAndId.fromDatacenterAndId(machineInDatacenter.getDatacenter(), objectId).slashEncode());
+         }
+      }).orNull();
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterId.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterId.java
new file mode 100644
index 0000000..5e0bbfc
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterId.java
@@ -0,0 +1,76 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.functions;
+
+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.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.predicates.NodePredicates;
+import org.jclouds.joyent.sdc.v6_5.compute.predicates.AllNodesInGroupTerminated;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class OrphanedGroupsByDatacenterId implements Function<Set<? extends NodeMetadata>, Multimap<String, String>> {
+   private final Predicate<DatacenterAndName> allNodesInGroupTerminated;
+
+   @Inject
+   protected OrphanedGroupsByDatacenterId(ComputeService computeService) {
+      this(new AllNodesInGroupTerminated(checkNotNull(computeService, "computeService")));
+   }
+
+   @VisibleForTesting
+   OrphanedGroupsByDatacenterId(Predicate<DatacenterAndName> allNodesInGroupTerminated) {
+      this.allNodesInGroupTerminated = checkNotNull(allNodesInGroupTerminated, "allNodesInGroupTerminated");
+   }
+
+   public Multimap<String, String> apply(Set<? extends NodeMetadata> deadNodes) {
+      Iterable<? extends NodeMetadata> nodesWithGroup = filter(deadNodes, NodePredicates.hasGroup());
+      Set<DatacenterAndName> datacenterAndGroupNames = ImmutableSet.copyOf(filter(transform(nodesWithGroup,
+               new Function<NodeMetadata, DatacenterAndName>() {
+
+                  @Override
+                  public DatacenterAndName apply(NodeMetadata input) {
+                     String datacenterId = input.getLocation().getId();
+                     return DatacenterAndName.fromDatacenterAndName(datacenterId, input.getGroup());
+                  }
+
+               }), allNodesInGroupTerminated));
+      Multimap<String, String> datacenterToDatacenterAndGroupNames = Multimaps.transformValues(Multimaps.index(datacenterAndGroupNames,
+               DatacenterAndName.DATACENTER_FUNCTION), DatacenterAndName.NAME_FUNCTION);
+      return datacenterToDatacenterAndGroupNames;
+   }
+
+}
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardware.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardware.java
new file mode 100644
index 0000000..2aa55db
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardware.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.joyent.sdc.v6_5.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.internal.VolumeImpl;
+import org.jclouds.domain.Location;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.PackageInDatacenter;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+
+/**
+ * A function for transforming the sdc specific PackageInDatacenter object to
+ * the generic Hardware object.
+ * 
+ * @author Adrian Cole
+ */
+public class PackageInDatacenterToHardware implements Function<PackageInDatacenter, Hardware> {
+
+   private final Supplier<Map<String, Location>> locationIndex;
+
+   @Inject
+   public PackageInDatacenterToHardware(Supplier<Map<String, Location>> locationIndex) {
+      this.locationIndex = checkNotNull(locationIndex, "locationIndex");
+   }
+
+   @Override
+   public Hardware apply(PackageInDatacenter pkgInDatacenter) {
+      Location location = locationIndex.get().get(pkgInDatacenter.getDatacenter());
+      checkState(location != null, "location %s not in locationIndex: %s", pkgInDatacenter.getDatacenter(),
+            locationIndex.get());
+      org.jclouds.joyent.sdc.v6_5.domain.Package pkg = pkgInDatacenter.get();
+      return new HardwareBuilder().id(pkgInDatacenter.slashEncode()).providerId(pkg.getName()).name(pkg.getName())
+            .ram(pkg.getMemorySizeMb())
+            // TODO: no api call to get processors.. either hard-code or
+            // calculate
+            .processor(new Processor(1, 1.0)).volume(new VolumeImpl(Float.valueOf(pkg.getDiskSizeGb()), true, true))
+            .location(location).build();
+   }
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/internal/KeyAndPrivateKey.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/internal/KeyAndPrivateKey.java
new file mode 100644
index 0000000..04c75a5
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/internal/KeyAndPrivateKey.java
@@ -0,0 +1,74 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+
+import com.google.common.base.Objects;
+
+/**
+ * @author Adrian Cole
+ */
+public class KeyAndPrivateKey {
+
+   public static KeyAndPrivateKey fromKeyAndPrivateKey(Key key, String privateKey) {
+      return new KeyAndPrivateKey(key, privateKey);
+   }
+
+   protected final Key key;
+   protected final String privateKey;
+
+   protected KeyAndPrivateKey(Key key, String privateKey) {
+      this.key = checkNotNull(key, "key");
+      this.privateKey = checkNotNull(privateKey, "privateKey");
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(key, privateKey);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      KeyAndPrivateKey other = (KeyAndPrivateKey) obj;
+      return Objects.equal(key, other.key) && Objects.equal(privateKey, other.privateKey);
+   }
+
+   public Key getKey() {
+      return key;
+   }
+
+   public String getPrivateKey() {
+      return privateKey;
+   }
+
+   @Override
+   public String toString() {
+      return "[key=" + key + ", privateKey=" + privateKey + "]";
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKey.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKey.java
new file mode 100644
index 0000000..ca19266
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKey.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.joyent.sdc.v6_5.compute.loaders;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.security.SecureRandom;
+import java.util.Map;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.crypto.SshKeys;
+import org.jclouds.joyent.sdc.v6_5.SDCClient;
+import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+import org.jclouds.logging.Logger;
+
+import com.google.common.cache.CacheLoader;
+import com.google.inject.Inject;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class CreateUniqueKey extends CacheLoader<DatacenterAndName, KeyAndPrivateKey> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+   protected final SDCClient sdcClient;
+   protected final GroupNamingConvention.Factory namingConvention;
+   protected final Crypto crypto;
+   protected final Provider<SecureRandom> secureRandom;
+
+   @Inject
+   public CreateUniqueKey(SDCClient sdcClient, GroupNamingConvention.Factory namingConvention, Crypto crypto, Provider<SecureRandom> secureRandom) {
+      this.sdcClient = checkNotNull(sdcClient, "sdcClient");
+      this.namingConvention = checkNotNull(namingConvention, "namingConvention");
+      this.crypto = checkNotNull(crypto, "crypto");
+      this.secureRandom = checkNotNull(secureRandom, "secureRandom");
+   }
+
+   @Override
+   public KeyAndPrivateKey load(DatacenterAndName datacenterAndName) {
+      String datacenterId = checkNotNull(datacenterAndName, "datacenterAndName").getDatacenter();
+      String prefix = datacenterAndName.getName();
+      
+      Map<String, String> keyPair = SshKeys.generate(crypto.rsaKeyPairGenerator(), secureRandom.get());
+      String publicKey = keyPair.get("public");
+      String privateKey = keyPair.get("private");
+
+      logger.debug(">> creating key datacenter(%s) prefix(%s)", datacenterId, prefix);
+
+      Key key = null;
+      while (key == null) {
+         String name = namingConvention.createWithoutPrefix().uniqueNameForGroup(prefix);
+         try {
+            key = sdcClient.getKeyClient().create(Key.builder().name(name).key(publicKey).build());
+         } catch (IllegalStateException e) {
+            logger.trace("error creating keypair named %s, %s", name, e.getMessage());
+         }
+      }
+
+      logger.debug("<< created key(%s)", key.getName());
+      return KeyAndPrivateKey.fromKeyAndPrivateKey(key, privateKey);
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptions.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptions.java
new file mode 100644
index 0000000..da6d02d
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptions.java
@@ -0,0 +1,337 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.options;
+
+import static com.google.common.base.Objects.equal;
+
+import java.util.Map;
+
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.scriptbuilder.domain.Statement;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Contains options supported in the {@code ComputeService#runNode} operation on the
+ * "joyent-sdc" provider. <h2>Usage</h2> The recommended way to instantiate a
+ * SDCTemplateOptions object is to statically import SDCTemplateOptions.* and invoke a static
+ * creation method followed by an instance mutator (if needed):
+ * <p/>
+ * <code>
+ * import static org.jclouds.aws.ec2.compute.options.SDCTemplateOptions.Builder.*;
+ * <p/>
+ * ComputeService client = // get connection
+ * templateBuilder.options(inboundPorts(22, 80, 8080, 443));
+ * Set<? extends NodeMetadata> set = client.createNodesInGroup(tag, 2, templateBuilder.build());
+ * <code>
+ * 
+ * @author Adrian Cole
+ */
+public class SDCTemplateOptions extends TemplateOptions implements Cloneable {
+   @Override
+   public SDCTemplateOptions clone() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      copyTo(options);
+      return options;
+   }
+
+   @Override
+   public void copyTo(TemplateOptions to) {
+      super.copyTo(to);
+      if (to instanceof SDCTemplateOptions) {
+         SDCTemplateOptions eTo = SDCTemplateOptions.class.cast(to);
+         eTo.generateKey(shouldGenerateKey());
+      }
+   }
+
+   protected boolean generateKey = false;
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o)
+         return true;
+      if (o == null || getClass() != o.getClass())
+         return false;
+      SDCTemplateOptions that = SDCTemplateOptions.class.cast(o);
+      return super.equals(that) && equal(this.generateKey, that.generateKey);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), generateKey);
+   }
+
+   @Override
+   public ToStringHelper string() {
+      ToStringHelper toString = super.string();
+      if (generateKey)
+         toString.add("generateKey", generateKey);
+      return toString;
+   }
+
+   /**
+    * @see #shouldGenerateKey()
+    */
+   public SDCTemplateOptions generateKey(boolean enable) {
+      this.generateKey = enable;
+      return this;
+   }
+   
+   /**
+    *
+    * @return true if auto generation of keys is enabled
+    */
+   public boolean shouldGenerateKey() {
+      return generateKey;
+   }
+   
+   public static class Builder {
+
+      /**
+       * @see SDCTemplateOptions#shouldGenerateKey() 
+       */
+      public static SDCTemplateOptions generateKey(boolean enable) {
+         return new SDCTemplateOptions().generateKey(enable);
+      }
+      
+      // methods that only facilitate returning the correct object type
+
+      /**
+       * @see TemplateOptions#inboundPorts
+       */
+      public static SDCTemplateOptions inboundPorts(int... ports) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return SDCTemplateOptions.class.cast(options.inboundPorts(ports));
+      }
+
+      /**
+       * @see TemplateOptions#port
+       */
+      public static SDCTemplateOptions blockOnPort(int port, int seconds) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return SDCTemplateOptions.class.cast(options.blockOnPort(port, seconds));
+      }
+
+      /**
+       * @see TemplateOptions#installPrivateKey
+       */
+      public static SDCTemplateOptions installPrivateKey(String rsaKey) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return SDCTemplateOptions.class.cast(options.installPrivateKey(rsaKey));
+      }
+
+      /**
+       * @see TemplateOptions#authorizePublicKey
+       */
+      public static SDCTemplateOptions authorizePublicKey(String rsaKey) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return SDCTemplateOptions.class.cast(options.authorizePublicKey(rsaKey));
+      }
+
+      /**
+       * @see TemplateOptions#userMetadata
+       */
+      public static SDCTemplateOptions userMetadata(Map<String, String> userMetadata) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return SDCTemplateOptions.class.cast(options.userMetadata(userMetadata));
+      }
+
+      /**
+       * @see TemplateOptions#overrideLoginUser
+       */
+      public static SDCTemplateOptions overrideLoginUser(String user) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return options.overrideLoginUser(user);
+      }
+
+      /**
+       * @see TemplateOptions#overrideLoginPassword
+       */
+      public static SDCTemplateOptions overrideLoginPassword(String password) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return options.overrideLoginPassword(password);
+      }
+
+      /**
+       * @see TemplateOptions#overrideLoginPrivateKey
+       */
+      public static SDCTemplateOptions overrideLoginPrivateKey(String privateKey) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return options.overrideLoginPrivateKey(privateKey);
+      }
+
+      /**
+       * @see TemplateOptions#overrideAuthenticateSudo
+       */
+      public static SDCTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return options.overrideAuthenticateSudo(authenticateSudo);
+      }
+
+      /**
+       * @see TemplateOptions#overrideLoginCredentials
+       */
+      public static SDCTemplateOptions overrideLoginCredentials(LoginCredentials credentials) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return options.overrideLoginCredentials(credentials);
+      }
+      
+      /**
+       * @see TemplateOptions#blockUntilRunning
+       */
+      public static SDCTemplateOptions blockUntilRunning(boolean blockUntilRunning) {
+         SDCTemplateOptions options = new SDCTemplateOptions();
+         return options.blockUntilRunning(blockUntilRunning);
+      }
+
+   }
+
+   // methods that only facilitate returning the correct object type
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions blockOnPort(int port, int seconds) {
+      return SDCTemplateOptions.class.cast(super.blockOnPort(port, seconds));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions inboundPorts(int... ports) {
+      return SDCTemplateOptions.class.cast(super.inboundPorts(ports));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions authorizePublicKey(String publicKey) {
+      return SDCTemplateOptions.class.cast(super.authorizePublicKey(publicKey));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions installPrivateKey(String privateKey) {
+      return SDCTemplateOptions.class.cast(super.installPrivateKey(privateKey));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions blockUntilRunning(boolean blockUntilRunning) {
+      return SDCTemplateOptions.class.cast(super.blockUntilRunning(blockUntilRunning));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions dontAuthorizePublicKey() {
+      return SDCTemplateOptions.class.cast(super.dontAuthorizePublicKey());
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions nameTask(String name) {
+      return SDCTemplateOptions.class.cast(super.nameTask(name));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions runAsRoot(boolean runAsRoot) {
+      return SDCTemplateOptions.class.cast(super.runAsRoot(runAsRoot));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions runScript(Statement script) {
+      return SDCTemplateOptions.class.cast(super.runScript(script));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions overrideLoginCredentials(LoginCredentials overridingCredentials) {
+      return SDCTemplateOptions.class.cast(super.overrideLoginCredentials(overridingCredentials));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions overrideLoginPassword(String password) {
+      return SDCTemplateOptions.class.cast(super.overrideLoginPassword(password));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions overrideLoginPrivateKey(String privateKey) {
+      return SDCTemplateOptions.class.cast(super.overrideLoginPrivateKey(privateKey));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions overrideLoginUser(String loginUser) {
+      return SDCTemplateOptions.class.cast(super.overrideLoginUser(loginUser));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) {
+      return SDCTemplateOptions.class.cast(super.overrideAuthenticateSudo(authenticateSudo));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions userMetadata(Map<String, String> userMetadata) {
+      return SDCTemplateOptions.class.cast(super.userMetadata(userMetadata));
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public SDCTemplateOptions userMetadata(String key, String value) {
+      return SDCTemplateOptions.class.cast(super.userMetadata(key, value));
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/predicates/AllNodesInGroupTerminated.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/predicates/AllNodesInGroupTerminated.java
new file mode 100644
index 0000000..ee20fdd
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/predicates/AllNodesInGroupTerminated.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.joyent.sdc.v6_5.compute.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.and;
+import static com.google.common.collect.Iterables.all;
+import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
+import static org.jclouds.compute.predicates.NodePredicates.inGroup;
+import static org.jclouds.compute.predicates.NodePredicates.locationId;
+
+import javax.inject.Inject;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Adrian Cole
+ */
+public class AllNodesInGroupTerminated implements Predicate<DatacenterAndName> {
+   private final ComputeService computeService;
+
+   
+   //TODO: TESTME
+   @Inject
+   public AllNodesInGroupTerminated(ComputeService computeService) {
+      this.computeService = checkNotNull(computeService, "computeService");
+   }
+
+   @Override
+   public boolean apply(DatacenterAndName input) {
+      return all(computeService.listNodesDetailsMatching(locationId(input.getDatacenter())), and(inGroup(input.getName()), TERMINATED));
+   }
+}
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/strategy/ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/strategy/ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java
new file mode 100644
index 0000000..0644756
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/strategy/ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java
@@ -0,0 +1,95 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.strategy;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.compute.config.CustomizationResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
+import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
+import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey;
+import org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Multimap;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Singleton
+public class ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet extends
+         CreateNodesWithGroupEncodedIntoNameThenAddToSet {
+
+   private final LoadingCache<DatacenterAndName, KeyAndPrivateKey> keyCache;
+
+   @Inject
+   protected ApplySDCTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet(
+            CreateNodeWithGroupEncodedIntoName addNodeWithTagStrategy,
+            ListNodesStrategy listNodesStrategy,
+            GroupNamingConvention.Factory namingConvention,
+            CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
+            @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor,
+            LoadingCache<DatacenterAndName, KeyAndPrivateKey> keyCache) {
+      super(addNodeWithTagStrategy, listNodesStrategy, namingConvention, executor,
+               customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
+      this.keyCache = checkNotNull(keyCache, "keyCache");
+   }
+
+   @Override
+   public Map<?, Future<Void>> execute(String group, int count, Template template, Set<NodeMetadata> goodNodes,
+         Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
+
+      Template mutableTemplate = template.clone();
+
+      SDCTemplateOptions templateOptions = SDCTemplateOptions.class.cast(mutableTemplate.getOptions());
+
+      assert template.getOptions().equals(templateOptions) : "options didn't clone properly";
+
+      String datacenter = mutableTemplate.getLocation().getId();
+
+      if (templateOptions.shouldGenerateKey()) {
+         KeyAndPrivateKey keyPair = keyCache.getUnchecked(DatacenterAndName.fromDatacenterAndName(datacenter, namingConvention.create()
+               .sharedNameForGroup(group)));
+         // in order to delete the key later
+         keyCache.asMap().put(DatacenterAndName.fromDatacenterAndName(datacenter, keyPair.getKey().getName()), keyPair);
+         templateOptions.overrideLoginPrivateKey(keyPair.getPrivateKey());
+      }
+      checkArgument(templateOptions.getRunScript() == null || templateOptions.getLoginPrivateKey() != null,
+            "when specifying runScript, you must either set overrideLoginPrivateKey, or generateKey(true)");
+      return super.execute(group, count, mutableTemplate, goodNodes, badNodes, customizationResponses);
+   }
+}
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/DatacentersAreZonesModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/DatacentersAreZonesModule.java
new file mode 100644
index 0000000..48b2c51
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/DatacentersAreZonesModule.java
@@ -0,0 +1,49 @@
+/**
+ * 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.joyent.sdc.v6_5.config;
+
+import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
+
+import org.jclouds.joyent.sdc.v6_5.features.DatacenterAsyncClient;
+import org.jclouds.joyent.sdc.v6_5.features.DatacenterClient;
+import org.jclouds.joyent.sdc.v6_5.suppliers.ZoneIdToURIFromDatacentersClient;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+import org.jclouds.location.suppliers.ZoneIdToURISupplier;
+import org.jclouds.location.suppliers.ZoneIdsSupplier;
+import org.jclouds.location.suppliers.derived.ZoneIdsFromZoneIdToURIKeySet;
+import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class DatacentersAreZonesModule extends AbstractModule {
+
+   @Override
+   protected void configure() {
+      // datacenter client is needed for obtaining zone ids
+      bindClientAndAsyncClient(binder(), DatacenterClient.class, DatacenterAsyncClient.class);
+      bind(ImplicitLocationSupplier.class).to(OnlyLocationOrFirstZone.class).in(Scopes.SINGLETON);
+      bind(ZoneIdToURISupplier.class).to(ZoneIdToURIFromDatacentersClient.class).in(Scopes.SINGLETON);
+      bind(ZoneIdsSupplier.class).to(ZoneIdsFromZoneIdToURIKeySet.class).in(Scopes.SINGLETON);
+   }
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java
index cc2e3f1..ae0974e 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCParserModule.java
@@ -38,7 +38,7 @@
    @Provides
    @Singleton
    public Map<Type, Object> provideCustomAdapterBindings() {
-      return ImmutableMap.<Type, Object> of(Machine.State.class, new SDCTypeAdapters.ServerStateAdapter(), Type.class,
+      return ImmutableMap.<Type, Object> of(Machine.State.class, new SDCTypeAdapters.MachineStateAdapter(), Type.class,
             new SDCTypeAdapters.SDCTypeAdapter());
    }
 
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java
index 71eaaea..e408831 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCProperties.java
@@ -25,4 +25,10 @@
  */
 public class SDCProperties {
 
+   /**
+    * Whenever a node is created, automatically generate keys for groups, as needed, also
+    * delete the key(s) when the last node in the group is destroyed.
+    */
+   public static final String AUTOGENERATE_KEYS = "jclouds.joyent-sdc.autogenerate-keys";
+
 }
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java
index 922dac6..5b27bdc 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/config/SDCRestClientModule.java
@@ -30,6 +30,8 @@
 import org.jclouds.joyent.sdc.v6_5.features.DatacenterClient;
 import org.jclouds.joyent.sdc.v6_5.features.DatasetAsyncClient;
 import org.jclouds.joyent.sdc.v6_5.features.DatasetClient;
+import org.jclouds.joyent.sdc.v6_5.features.KeyAsyncClient;
+import org.jclouds.joyent.sdc.v6_5.features.KeyClient;
 import org.jclouds.joyent.sdc.v6_5.features.MachineAsyncClient;
 import org.jclouds.joyent.sdc.v6_5.features.MachineClient;
 import org.jclouds.joyent.sdc.v6_5.features.PackageAsyncClient;
@@ -50,8 +52,11 @@
 @ConfiguresRestClient
 public class SDCRestClientModule extends RestClientModule<SDCClient, SDCAsyncClient> {
    public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
-         .put(DatacenterClient.class, DatacenterAsyncClient.class).put(MachineClient.class, MachineAsyncClient.class)
-         .put(DatasetClient.class, DatasetAsyncClient.class).put(PackageClient.class, PackageAsyncClient.class).build();
+         .put(DatacenterClient.class, DatacenterAsyncClient.class)
+         .put(KeyClient.class, KeyAsyncClient.class)
+         .put(MachineClient.class, MachineAsyncClient.class)
+         .put(DatasetClient.class, DatasetAsyncClient.class)
+         .put(PackageClient.class, PackageAsyncClient.class).build();
 
    public SDCRestClientModule() {
       super(DELEGATE_MAP);
@@ -70,4 +75,5 @@
       bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(SDCErrorHandler.class);
       bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(SDCErrorHandler.class);
    }
+   
 }
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Key.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Key.java
new file mode 100644
index 0000000..1933691
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Key.java
@@ -0,0 +1,109 @@
+package org.jclouds.joyent.sdc.v6_5.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Date;
+
+import com.google.common.base.Objects;
+
+/**
+ * Keys are the means by which you operate on your SSH/signing keys. Currently
+ * CloudAPI supports uploads of public keys in the OpenSSH format.
+ * 
+ * @author Adrian Cole
+ * @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/#keys" >docs</a>
+ */
+public class Key implements Comparable<Key> {
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return builder().fromKey(this);
+   }
+   
+   public static class Builder {
+      private String name;
+      private String key;
+      private Date created;
+
+      public Builder name(String name) {
+         this.name = name;
+         return this;
+      }
+
+      public Builder key(String key) {
+         this.key = key;
+         return this;
+      }
+
+      public Builder created(Date created) {
+         this.created = created;
+         return this;
+      }
+
+      public Key build() {
+         return new Key(name, key, created);
+      }
+
+      public Builder fromKey(Key in) {
+         return name(in.getName()).key(in.get()).created(in.getCreated());
+      }
+   }
+
+   protected final String name;
+   protected final String key;
+   protected final Date created;
+
+   public Key(String name, String key, Date created) {
+      this.name = checkNotNull(name, "name");
+      this.key = checkNotNull(key, "key: OpenSSH formatted public key");
+      this.created = created;
+   }
+
+   /**
+    * Name for this key
+    */
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * OpenSSH formatted public key
+    */
+   public String get() {
+      return key;
+   }
+
+   public Date getCreated() {
+      return created;
+   }
+
+   @Override
+   public int compareTo(Key other) {
+      return name.compareTo(other.getName());
+   }
+
+   @Override
+   public boolean equals(Object object) {
+      if (this == object) {
+         return true;
+      }
+      if (object instanceof Key) {
+         return Objects.equal(name, ((Key) object).name);
+      } else {
+         return false;
+      }
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(name);
+   }
+
+   @Override
+   public String toString() {
+      return String.format("[name=%s, key=%s, created=%s]", name, key, created);
+   }
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java
index 7a4df0c..5bbe326 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/Machine.java
@@ -24,10 +24,18 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.jclouds.domain.JsonBall;
+import org.jclouds.joyent.sdc.v6_5.reference.MetadataKeys;
+import org.jclouds.json.Json;
+
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.CaseFormat;
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import com.google.gson.annotations.SerializedName;
 
 /**
@@ -39,7 +47,7 @@
 public class Machine implements Comparable<Machine> {
 
    public static enum State {
-      PUBLISHING, RUNNING, STOPPED, UNRECOGNIZED;
+      PROVISIONING, RUNNING, STOPPING, STOPPED, OFFLINE, DELETED, UNRECOGNIZED;
 
       public static State fromValue(String state) {
          try {
@@ -62,6 +70,10 @@
    public static Builder builder() {
       return new Builder();
    }
+   
+   public Builder toBuilder() {
+      return new Builder().fromMachine(this);
+   }
 
    public static class Builder {
       private String id;
@@ -74,7 +86,7 @@
       private Set<String> ips;
       private Date created;
       private Date updated;
-      private Map<String, String> metadata = ImmutableMap.of();
+      private Map<String, JsonBall> metadata = ImmutableMap.of();
 
       public Builder id(String id) {
          this.id = id;
@@ -129,7 +141,7 @@
       /**
        * @see Machine#getMetadata()
        */
-      public Builder metadata(Map<String, String> metadata) {
+      public Builder metadata(Map<String, JsonBall> metadata) {
          this.metadata = metadata;
          return this;
       }
@@ -139,9 +151,9 @@
       }
 
       public Builder fromMachine(Machine in) {
-         return id(in.getId()).name(in.getName()).type(in.getType()).state(in.getState()).dataset(in.getDataset())
+         return id(in.getId()).name(in.getName()).type(in.getType()).state(in.getState()).dataset(in.get())
                .memorySizeMb(in.getMemorySizeMb()).diskSizeGb(in.getDiskSizeGb()).ips(in.getIps())
-               .metadata(in.getMetadata()).created(in.getCreated()).updated(in.getUpdated());
+               .metadata(in.metadata).created(in.getCreated()).updated(in.getUpdated());
       }
    }
 
@@ -169,7 +181,7 @@
    protected final Date updated;
 
    // metadata Object[String => String] Any "extra" metadata this machine has
-   private final Map<String, String> metadata;
+   private final Map<String, JsonBall> metadata;
 
    @Override
    public int compareTo(Machine other) {
@@ -177,7 +189,7 @@
    }
 
    public Machine(String id, String name, Type type, State state, String dataset, int memorySizeMb, int diskSizeGb,
-         Set<String> ips, Date created, Date updated, final Map<String, String> metadata) {
+         Set<String> ips, Date created, Date updated, final Map<String, JsonBall> metadata) {
       super();
       this.id = id;
       this.name = name;
@@ -208,7 +220,7 @@
       return state;
    }
 
-   public String getDataset() {
+   public String get() {
       return dataset;
    }
 
@@ -232,10 +244,39 @@
       return updated;
    }
 
-   public Map<String, String> getMetadata() {
-      return metadata;
+   /**
+    * If the value is a string, it will be quoted, as that's how json strings are represented. 
+    * 
+    * @return key to a json literal of the value
+    * @see MetadataKeys#valueType
+    * @see Json#fromJson
+    */
+   public Map<String, String> getMetadataAsJsonLiterals() {
+      return Maps.transformValues(metadata, Functions.toStringFunction());
    }
 
+   /**
+    * Note!! metadata can contain arbitrarily complex values.  If the value has structure, you should use {@link #getMetadataAsJsonLiterals}
+    * 
+    * @return metadata
+    */
+   public Map<String, String> getMetadata() {
+      return Maps.transformValues(metadata, Functions.compose(Functions.toStringFunction(), unquoteString));
+   }
+
+   @VisibleForTesting
+   static final Function<JsonBall, String> unquoteString = new Function<JsonBall, String>() {
+
+      @Override
+      public String apply(JsonBall input) {
+         String value = input.toString();
+         if (value.length() >= 2 && value.charAt(0) == '"' && value.charAt(input.length() - 1) == '"')
+            return value.substring(1, input.length() - 1);
+         return value;
+      }
+
+   };
+   
    @Override
    public boolean equals(Object object) {
       if (this == object) {
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndId.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndId.java
new file mode 100644
index 0000000..df04e1d
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndId.java
@@ -0,0 +1,88 @@
+/**
+ * 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.joyent.sdc.v6_5.domain.datacenterscoped;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Adrian Cole
+ */
+public class DatacenterAndId {
+   public static DatacenterAndId fromSlashEncoded(String id) {
+      Iterable<String> parts = Splitter.on('/').split(checkNotNull(id, "id"));
+      checkArgument(Iterables.size(parts) == 2, "id must be in format datacenterId/id");
+      return new DatacenterAndId(Iterables.get(parts, 0), Iterables.get(parts, 1));
+   }
+
+   public static DatacenterAndId fromDatacenterAndId(String datacenterId, String id) {
+      return new DatacenterAndId(datacenterId, id);
+   }
+
+   private static String slashEncodeDatacenterAndId(String datacenterId, String id) {
+      return checkNotNull(datacenterId, "datacenterId") + "/" + checkNotNull(id, "id");
+   }
+
+   public String slashEncode() {
+      return slashEncodeDatacenterAndId(datacenterId, id);
+   }
+
+   protected final String datacenterId;
+   protected final String id;
+
+   protected DatacenterAndId(String datacenterId, String id) {
+      this.datacenterId = checkNotNull(datacenterId, "datacenterId");
+      this.id = checkNotNull(id, "id");
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(datacenterId, id);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      DatacenterAndId other = (DatacenterAndId) obj;
+      return Objects.equal(datacenterId, other.datacenterId) && Objects.equal(id, other.id);
+   }
+
+   public String getDatacenter() {
+      return datacenterId;
+   }
+
+   public String getId() {
+      return id;
+   }
+
+   @Override
+   public String toString() {
+      return "[datacenterId=" + datacenterId + ", id=" + id + "]";
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndName.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndName.java
new file mode 100644
index 0000000..158c384
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatacenterAndName.java
@@ -0,0 +1,113 @@
+/**
+ * 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.joyent.sdc.v6_5.domain.datacenterscoped;
+
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Splitter;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.Iterables;
+
+/**
+ * Helpful when looking for resources by datacenter and name
+ * 
+ * @author Adrian Cole
+ */
+public class DatacenterAndName {
+   
+   public final static Function<DatacenterAndName, String> NAME_FUNCTION = new Function<DatacenterAndName, String>(){
+
+      @Override
+      public String apply(DatacenterAndName input) {
+         return input.getName();
+      }
+      
+   };
+   
+   public final static Function<DatacenterAndName, String> DATACENTER_FUNCTION = new Function<DatacenterAndName, String>(){
+
+      @Override
+      public String apply(DatacenterAndName input) {
+         return input.getDatacenter();
+      }
+      
+   };
+   
+   public static DatacenterAndName fromSlashEncoded(String name) {
+      Iterable<String> parts = Splitter.on('/').split(checkNotNull(name, "name"));
+      checkArgument(Iterables.size(parts) == 2, "name must be in format datacenterId/name");
+      return new DatacenterAndName(Iterables.get(parts, 0), Iterables.get(parts, 1));
+   }
+
+   public static DatacenterAndName fromDatacenterAndName(String datacenterId, String name) {
+      return new DatacenterAndName(datacenterId, name);
+   }
+
+   private static String slashEncodeDatacenterAndName(String datacenterId, String name) {
+      return checkNotNull(datacenterId, "datacenterId") + "/" + checkNotNull(name, "name");
+   }
+
+   public String slashEncode() {
+      return slashEncodeDatacenterAndName(datacenterId, name);
+   }
+
+   protected final String datacenterId;
+   protected final String name;
+
+   protected DatacenterAndName(String datacenterId, String name) {
+      this.datacenterId = checkNotNull(datacenterId, "datacenterId");
+      this.name = checkNotNull(name, "name");
+   }
+
+   public String getDatacenter() {
+      return datacenterId;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o)
+         return true;
+      if (o == null || getClass() != o.getClass())
+         return false;
+      DatacenterAndName that = DatacenterAndName.class.cast(o);
+      return equal(this.datacenterId, that.datacenterId) && equal(this.name, that.name);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(datacenterId, name);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("datacenterId", datacenterId).add("name", name);
+   }
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatasetInDatacenter.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatasetInDatacenter.java
new file mode 100644
index 0000000..4e3e0e6
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/DatasetInDatacenter.java
@@ -0,0 +1,48 @@
+/**
+ * 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.joyent.sdc.v6_5.domain.datacenterscoped;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.joyent.sdc.v6_5.domain.Dataset;
+
+/**
+ * @author Adrian Cole
+ */
+public class DatasetInDatacenter extends DatacenterAndId {
+   protected final Dataset dataset;
+
+   public DatasetInDatacenter(Dataset dataset, String datacenterId) {
+      super(datacenterId, checkNotNull(dataset, "dataset").getId());
+      this.dataset = dataset;
+   }
+
+   public Dataset get() {
+      return dataset;
+   }
+
+   // superclass hashCode/equals are good enough, and help us use DatacenterAndId and DatasetInDatacenter
+   // interchangeably as Map keys
+
+   @Override
+   public String toString() {
+      return "[dataset=" + dataset + ", datacenterId=" + datacenterId + "]";
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/KeyInDatacenter.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/KeyInDatacenter.java
new file mode 100644
index 0000000..42a80a0
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/KeyInDatacenter.java
@@ -0,0 +1,48 @@
+/**
+ * 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.joyent.sdc.v6_5.domain.datacenterscoped;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+
+/**
+ * @author Adrian Cole
+ */
+public class KeyInDatacenter extends DatacenterAndName {
+   protected final Key key;
+
+   public KeyInDatacenter(Key key, String datacenterId) {
+      super(datacenterId, checkNotNull(key, "key").getName());
+      this.key = key;
+   }
+
+   public Key get() {
+      return key;
+   }
+
+   // superclass hashCode/equals are good enough, and help us use DatacenterAndId and PackageInDatacenter
+   // interchangeably as Map keys
+
+   @Override
+   public String toString() {
+      return "[key=" + key + ", datacenterId=" + datacenterId + "]";
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/MachineInDatacenter.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/MachineInDatacenter.java
new file mode 100644
index 0000000..1f0fb23
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/MachineInDatacenter.java
@@ -0,0 +1,48 @@
+/**
+ * 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.joyent.sdc.v6_5.domain.datacenterscoped;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.joyent.sdc.v6_5.domain.Machine;
+
+/**
+ * @author Adrian Cole
+ */
+public class MachineInDatacenter extends DatacenterAndId {
+   protected final Machine machine;
+
+   public MachineInDatacenter(Machine machine, String datacenterId) {
+      super(datacenterId, checkNotNull(machine, "machine").getId());
+      this.machine = machine;
+   }
+
+   public Machine get() {
+      return machine;
+   }
+
+   // superclass hashCode/equals are good enough, and help us use DatacenterAndId and MachineInDatacenter
+   // interchangeably as Map keys
+
+   @Override
+   public String toString() {
+      return "[machine=" + machine + ", datacenterId=" + datacenterId + "]";
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/PackageInDatacenter.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/PackageInDatacenter.java
new file mode 100644
index 0000000..3a155c1
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/domain/datacenterscoped/PackageInDatacenter.java
@@ -0,0 +1,46 @@
+/**
+ * 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.joyent.sdc.v6_5.domain.datacenterscoped;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * @author Adrian Cole
+ */
+public class PackageInDatacenter extends DatacenterAndName {
+   protected final org.jclouds.joyent.sdc.v6_5.domain.Package pkg;
+
+   public PackageInDatacenter(org.jclouds.joyent.sdc.v6_5.domain.Package pkg, String datacenterId) {
+      super(datacenterId, checkNotNull(pkg, "pkg").getName());
+      this.pkg = pkg;
+   }
+
+   public org.jclouds.joyent.sdc.v6_5.domain.Package get() {
+      return pkg;
+   }
+
+   // superclass hashCode/equals are good enough, and help us use DatacenterAndId and PackageInDatacenter
+   // interchangeably as Map keys
+
+   @Override
+   public String toString() {
+      return "[pkg=" + pkg + ", datacenterId=" + datacenterId + "]";
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java
index 80eff51..c76ce88 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetAsyncClient.java
@@ -32,20 +32,20 @@
 @RequestFilters(BasicAuthentication.class)
 public interface DatasetAsyncClient {
    /**
-    * @see DatasetClient#listMachines
+    * @see DatasetClient#list
     */
    @GET
    @Path("/my/datasets")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Dataset>> listDatasets();
+   ListenableFuture<Set<Dataset>> list();
 
    /**
-    * @see DatasetClient#getMachineDetails
+    * @see DatasetClient#get
     */
    @GET
    @Path("/my/datasets/{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Dataset> getDataset(@PathParam("id") String id);
+   ListenableFuture<Dataset> get(@PathParam("id") String id);
 }
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java
index c33fd80..eaf99d5 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClient.java
@@ -22,7 +22,7 @@
     * 
     * @return
     */
-   Set<Dataset> listDatasets();
+   Set<Dataset> list();
 
    /**
     * Gets an individual dataset by id.
@@ -31,5 +31,5 @@
     *           the id of the dataset
     * @return
     */
-   Dataset getDataset(String id);
+   Dataset get(String id);
 }
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/KeyAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/KeyAsyncClient.java
new file mode 100644
index 0000000..975347b
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/KeyAsyncClient.java
@@ -0,0 +1,69 @@
+package org.jclouds.joyent.sdc.v6_5.features;
+
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.joyent.sdc.v6_5.binders.BindKeyToJsonPayload;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.Headers;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SkipEncoding;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * @author Adrian Cole
+ * @see KeyClient
+ * @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/#keys">api doc</a>
+ */
+@SkipEncoding({ '/', '=' })
+@Headers(keys = "X-Api-Version", values = "{jclouds.api-version}")
+@RequestFilters(BasicAuthentication.class)
+public interface KeyAsyncClient {
+   /**
+    * @see KeyClient#list
+    */
+   @GET
+   @Path("/my/keys")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   ListenableFuture<Set<Key>> list();
+
+   /**
+    * @see KeyClient#get
+    */
+   @GET
+   @Path("/my/keys/{name}")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<Key> get(@PathParam("name") String name);
+   
+   /**
+    * @see KeyClient#create
+    */
+   @POST
+   @Path("/my/keys")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<Key> create(@BinderParam(BindKeyToJsonPayload.class) Key key);
+   
+   /**
+    * @see KeyClient#delete
+    */
+   @DELETE
+   @Consumes(MediaType.APPLICATION_JSON)
+   @Path("/my/keys/{name}")
+   ListenableFuture<Void> delete(@PathParam("name") String name);
+   
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/KeyClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/KeyClient.java
new file mode 100644
index 0000000..ad540b6
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/KeyClient.java
@@ -0,0 +1,40 @@
+package org.jclouds.joyent.sdc.v6_5.features;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+
+/**
+ * Keys are the means by which you operate on your SSH/signing keys. Currently
+ * CloudAPI supports uploads of public keys in the OpenSSH format.
+ * 
+ * @author Adrian Cole
+ * @see KeyAsyncClient
+ * @see <a href="http://apidocs.joyent.com/sdcapidoc/cloudapi/#keys">api doc</a>
+ */
+@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
+public interface KeyClient {
+
+   /**
+    * Lists all public keys we have on record for the specified account.
+    */
+   Set<Key> list();
+
+   /**
+    * Retrieves an individual key record.
+    */
+   Key get(String name);
+
+   /**
+    * Uploads a new OpenSSH key to SmartDataCenter for use in SSH and HTTP
+    * signing.
+    */
+   Key create(Key key);
+
+   /**
+    * Deletes an SSH key by name.
+    */
+   void delete(String name);
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java
index b32ab43..67281e1 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineAsyncClient.java
@@ -27,14 +27,14 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.http.filters.BasicAuthentication;
 import org.jclouds.joyent.sdc.v6_5.domain.Machine;
-import org.jclouds.joyent.sdc.v6_5.options.CreateServerOptions;
+import org.jclouds.joyent.sdc.v6_5.options.CreateMachineOptions;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.Headers;
-import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.Payload;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
@@ -58,81 +58,86 @@
 public interface MachineAsyncClient {
 
    /**
-    * @see MachineClient#listMachines
+    * @see MachineClient#list
     */
    @GET
    @Path("/my/machines")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Machine>> listMachines();
+   ListenableFuture<Set<Machine>> list();
 
    /**
-    * @see MachineClient#getMachineDetails
+    * @see MachineClient#get
     */
    @GET
    @Path("/my/machines/{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Machine> getMachine(@PathParam("id") String id);
-
+   ListenableFuture<Machine> get(@PathParam("id") String id);
+   
+   /**
+    * @see MachineClient#createWithDataset(String)
+    */
+   @POST
+   @Path("/my/machines")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<Machine> createWithDataset(@QueryParam("dataset") String datasetURN);
+   
 	/**
-	 * @see MachineClient#createMachine
+	 * @see MachineClient#createWithDataset(String, CreateMachineOptions)
 	 */
 	@POST
-	@Path("/my/machines")
-	@Consumes(MediaType.APPLICATION_JSON)
-	@MapBinder(CreateServerOptions.class)
-	ListenableFuture<Machine> createMachine(@PayloadParam("name") String name,
-			@PayloadParam("package") String packageSDC,
-			@PayloadParam("dataset") String dataset,CreateServerOptions... options);
+   @Path("/my/machines")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<Machine> createWithDataset(@QueryParam("dataset") String datasetURN, CreateMachineOptions options);
 
 	/**
-	 * @see MachineClient#stopMachine
+	 * @see MachineClient#stop
 	 */
 	@POST
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Produces(MediaType.APPLICATION_FORM_URLENCODED)
 	@Path("/my/machines/{id}")
 	@Payload("action=stop")
-	ListenableFuture<Void> stopMachine(@PathParam("id") String id);
+	ListenableFuture<Void> stop(@PathParam("id") String id);
 	
 	/**
-	 * @see MachineClient#startMachine
+	 * @see MachineClient#start
 	 */
 	@POST
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Produces(MediaType.APPLICATION_FORM_URLENCODED)
 	@Path("/my/machines/{id}")
 	@Payload("action=start")
-	ListenableFuture<Void> startMachine(@PathParam("id") String id);
+	ListenableFuture<Void> start(@PathParam("id") String id);
 	
 	/**
-	 * @see MachineClient#rebootMachine
+	 * @see MachineClient#reboot
 	 */
 	@POST
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Produces(MediaType.APPLICATION_FORM_URLENCODED)
 	@Path("/my/machines/{id}")
 	@Payload("action=reboot")
-	ListenableFuture<Void> rebootMachine(@PathParam("id") String id);
+	ListenableFuture<Void> reboot(@PathParam("id") String id);
 	
 	/**
-	 * @see MachineClient#resizeMachine
+	 * @see MachineClient#resize
 	 */
 	@POST
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Produces(MediaType.APPLICATION_FORM_URLENCODED)
 	@Path("/my/machines/{id}")
 	@Payload("action=resize&package={package}")
-	ListenableFuture<Void> resizeMachine(@PathParam("id") String id,@PayloadParam("package") String packageSDC);
+	ListenableFuture<Void> resize(@PathParam("id") String id,@PayloadParam("package") String packageSDC);
 	
 	/**
-	 * @see MachineClient#deleteMachine
+	 * @see MachineClient#delete
 	 */
 	@DELETE
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("/my/machines/{id}")
-	ListenableFuture<Void> deleteMachine(@PathParam("id") String id);
+	ListenableFuture<Void> delete(@PathParam("id") String id);
 	
 	
 	
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java
index 5cec1ed..64ccc6d 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/MachineClient.java
@@ -23,7 +23,7 @@
 
 import org.jclouds.concurrent.Timeout;
 import org.jclouds.joyent.sdc.v6_5.domain.Machine;
-import org.jclouds.joyent.sdc.v6_5.options.CreateServerOptions;
+import org.jclouds.joyent.sdc.v6_5.options.CreateMachineOptions;
 
 /**
  * Provides synchronous access to Machine.
@@ -39,9 +39,9 @@
    /**
     * Lists all machines we have on record for your account.
     * 
-    * @return an account's associated server objects.
+    * @return an account's associated machine objects.
     */
-   Set<Machine> listMachines();
+   Set<Machine> list();
 
    /**
     * Gets the details for an individual machine.
@@ -50,21 +50,47 @@
     *           the id of the machine
     * @return
     */
-   Machine getMachine(String id);
+   Machine get(String id);
 
    /**
+    * Allows you to provision a machine. Note that if you do not specify a
+    * package, you'll get the datacenter defaults for it. If
+    * you do not specify a name, CloudAPI will generate a random one for you.
+    * Your machine will initially be not available for login (SmartDataCenter
+    * must provision and boot it). You can poll {@link #get} for status. When the
+    * state field is equal to running, you can login.
     * 
-    * @param name
-    *           friendly name for this machine; default is a randomly generated name
-    * @param packageSDC
-    *           Name of the package to use on provisioning; default is indicated in ListPackages
-    * @param dataset
-    *           dataset URN; default is indicated in ListDatasets
-    * @param options
-    *           optional parameters to be passed into the machine creation request
+    * <p/>
+    * With regards to login, if the machine is of type smartmachine, you can use
+    * any of the SSH keys managed under the keys section of CloudAPI to login,
+    * as any POSIX user on the OS. You can add/remove them over time, and the
+    * machine will automatically work with that set.
+    * 
+    * <p/>
+    * If the the machine is a virtualmachine, and of a UNIX-derived OS (e.g.
+    * Linux), you must have keys uploaded before provisioning; that entire set
+    * of keys will be written out to /root/.ssh/authorized_keys, and you can ssh
+    * in using one of those. Changing the keys over time under your account will
+    * not affect the running virtual machine in any way; those keys are
+    * statically written at provisioning-time only, and you will need to
+    * manually manage them.
+    * 
+    * <p/>
+    * If the dataset you create a machine from is set to generate passwords for
+    * you, the username/password pairs will be returned in the metadata response
+    * as a nested object, like:
+    * 
+    * @param datasetURN urn of the dataset to install
+    * 
     * @return the newly created machine
     */
-   Machine createMachine(String name, String packageSDC, String dataset, CreateServerOptions... options);
+   Machine createWithDataset(String datasetURN, CreateMachineOptions options);
+   
+   /**
+    * 
+    * @see #createWithDataset(CreateMachineOptions)
+    */
+   Machine createWithDataset(String datasetURN);
 
    /**
     * Allows you to shut down a machine.
@@ -72,7 +98,7 @@
     * @param id
     *           the id of the machine to stop
     */
-   void stopMachine(String id);
+   void stop(String id);
 
    /**
     * Allows you to boot up a machine.
@@ -80,7 +106,7 @@
     * @param id
     *           the id of the machine to start
     */
-   void startMachine(String id);
+   void start(String id);
 
    /**
     * Allows you to reboot a machine.
@@ -88,7 +114,7 @@
     * @param id
     *           the id of the machine to reboot
     */
-   void rebootMachine(String id);
+   void reboot(String id);
 
    /**
     * Allows you to resize a machine. (Works only for smart machines)
@@ -98,14 +124,15 @@
     * @param packageSDC
     *           the package to use for the machine
     */
-   void resizeMachine(String id, String packageSDC);
+   void resize(String id, String packageSDC);
 
    /**
-    * Allows you to delete a machine (the machine must be stopped before it can be deleted).
+    * Allows you to delete a machine (the machine must be stopped before it can
+    * be deleted).
     * 
     * @param id
     *           the id of the machine to delete
     */
-   void deleteMachine(String id);
+   void delete(String id);
 
 }
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java
index 7a316da..eab42d6 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageAsyncClient.java
@@ -31,20 +31,20 @@
 @RequestFilters(BasicAuthentication.class)
 public interface PackageAsyncClient {
    /**
-    * @see PackageClient#listPackages
+    * @see PackageClient#list
     */
    @GET
    @Path("/my/packages")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<org.jclouds.joyent.sdc.v6_5.domain.Package>> listPackages();
+   ListenableFuture<Set<org.jclouds.joyent.sdc.v6_5.domain.Package>> list();
 
    /**
-    * @see PackageClient#getPackageDetails
+    * @see PackageClient#get
     */
    @GET
    @Path("/my/packages/{name}")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<org.jclouds.joyent.sdc.v6_5.domain.Package> getPackage(@PathParam("name") String name);
+   ListenableFuture<org.jclouds.joyent.sdc.v6_5.domain.Package> get(@PathParam("name") String name);
 }
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java
index 4f58da8..4b02b8f 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/features/PackageClient.java
@@ -21,7 +21,7 @@
     * 
     * @return
     */
-   Set<org.jclouds.joyent.sdc.v6_5.domain.Package> listPackages();
+   Set<org.jclouds.joyent.sdc.v6_5.domain.Package> list();
 
    /**
     * Gets an individual package by id.
@@ -30,5 +30,5 @@
     *           the name of the package
     * @return
     */
-   org.jclouds.joyent.sdc.v6_5.domain.Package getPackage(String name);
+   org.jclouds.joyent.sdc.v6_5.domain.Package get(String name);
 }
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java
index ecfe62e..78c6066 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/functions/internal/SDCTypeAdapters.java
@@ -28,11 +28,11 @@
 import com.google.gson.stream.JsonWriter;
 
 /**
- * @author Adam Lowe
+ * @author Adrian Cole
  */
 public class SDCTypeAdapters {
 
-   public static class ServerStateAdapter extends TypeAdapter<Machine.State> {
+   public static class MachineStateAdapter extends TypeAdapter<Machine.State> {
       @Override
       public void write(JsonWriter writer, Machine.State value) throws IOException {
          writer.value(value.value());
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateMachineOptions.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateMachineOptions.java
new file mode 100644
index 0000000..e0f5292
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateMachineOptions.java
@@ -0,0 +1,146 @@
+/**
+ * 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.joyent.sdc.v6_5.options;
+
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+import org.jclouds.joyent.sdc.v6_5.features.PackageClient;
+import org.jclouds.util.Maps2;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+
+/**
+ * 
+ * @author Adrian Cole
+ * 
+ */
+public class CreateMachineOptions extends BaseHttpRequestOptions {
+   private String name;
+   private String pkg;
+   private Map<String, String> metadata = ImmutableMap.of();
+
+   @Override
+   public boolean equals(Object object) {
+      if (this == object) {
+         return true;
+      }
+      if (object instanceof CreateMachineOptions) {
+         final CreateMachineOptions other = CreateMachineOptions.class.cast(object);
+         return equal(name, other.name) && equal(pkg, other.pkg) && equal(metadata, other.metadata);
+      } else {
+         return false;
+      }
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(name, pkg, metadata);
+   }
+
+   @Override
+   public String toString() {
+      ToStringHelper toString = Objects.toStringHelper("").omitNullValues();
+      toString.add("name", name).add("package", name);
+      if (metadata.size() > 0)
+         toString.add("metadata", metadata);
+      return toString.toString();
+   }
+
+   @Override
+   public Multimap<String, String> buildQueryParameters() {
+      Multimap<String, String> params = super.buildQueryParameters();
+      if (name != null)
+         params.put("name", name);
+      if (pkg != null)
+         params.put("package", pkg);
+      params.putAll(Multimaps.forMap(Maps2.transformKeys(metadata, new Function<String, String>() {
+
+         @Override
+         public String apply(String input) {
+            return "metadata." + input;
+         }
+
+      })));
+      return params;
+   }
+
+   /**
+    * friendly name for this machine; default is a randomly generated name
+    */
+   public CreateMachineOptions name(String name) {
+      this.name = checkNotNull(name, "name");
+      return this;
+   }
+
+   /**
+    * Name of the package to use on provisioning; default is indicated in
+    * {@link PackageClient#list}
+    */
+   public CreateMachineOptions packageName(String packageName) {
+      this.pkg = checkNotNull(packageName, "packageName");
+      return this;
+   }
+
+   /**
+    * An arbitrary set of metadata key/value pairs.
+    */
+   public CreateMachineOptions metadata(Map<String, String> metadata) {
+      checkNotNull(metadata, "metadata");
+      this.metadata = ImmutableMap.copyOf(metadata);
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see CreateMachineOptions#name
+       */
+      public static CreateMachineOptions name(String name) {
+         CreateMachineOptions options = new CreateMachineOptions();
+         return options.name(name);
+      }
+
+      /**
+       * @see CreateMachineOptions#packageName
+       */
+      public static CreateMachineOptions packageName(String packageName) {
+         CreateMachineOptions options = new CreateMachineOptions();
+         return options.packageName(packageName);
+      }
+
+      /**
+       * @see CreateMachineOptions#metadata(Map<String, String>)
+       */
+      public static CreateMachineOptions metadata(Map<String, String> metadata) {
+         CreateMachineOptions options = new CreateMachineOptions();
+         return options.metadata(metadata);
+      }
+
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateServerOptions.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateServerOptions.java
deleted file mode 100644
index 631b69f..0000000
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/options/CreateServerOptions.java
+++ /dev/null
@@ -1,151 +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.joyent.sdc.v6_5.options;
-
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Map;
-
-import javax.inject.Inject;
-
-import org.jclouds.http.HttpRequest;
-import org.jclouds.rest.MapBinder;
-import org.jclouds.rest.binders.BindToJsonPayload;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.collect.ImmutableMap;
-import com.google.gson.annotations.SerializedName;
-
-/**
- * 
- * @author Gerald Pereira
- * 
- */
-public class CreateServerOptions implements MapBinder {
-   @Inject
-   private BindToJsonPayload jsonBinder;
-
-   private Map<String, String> metadata = ImmutableMap.of();
-   private Map<String, String> tag = ImmutableMap.of();
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof CreateServerOptions) {
-         final CreateServerOptions other = CreateServerOptions.class.cast(object);
-         return equal(tag, tag) && equal(metadata, other.metadata);
-      } else {
-         return false;
-      }
-   }
-
-   @Override
-   public int hashCode() {
-      return Objects.hashCode(tag, metadata);
-   }
-
-   protected ToStringHelper string() {
-      return toStringHelper("").add("metadata", metadata).add("tag", tag);
-   }
-
-   @Override
-   public String toString() {
-      return string().toString();
-   }
-
-   @Override
-   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
-      return jsonBinder.bindToRequest(request, input);
-   }
-
-   @Override
-   public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
-      MachineRequest machine = new MachineRequest(checkNotNull(postParams.get("name"), "name parameter not present")
-               .toString(), checkNotNull(postParams.get("package"), "package parameter not present").toString(),
-               checkNotNull(postParams.get("dataset"), "dataset parameter not present").toString());
-
-      if (metadata.size() > 0)
-         machine.metadata = metadata;
-      if (tag.size() > 0)
-         machine.tag = tag;
-
-      return bindToRequest(request, machine);
-   }
-
-   /**
-    * An arbitrary set of metadata key/value pairs can be set at provision time, but they must be
-    * prefixed with "metadata."
-    */
-   public CreateServerOptions metadata(Map<String, String> metadata) {
-      checkNotNull(metadata, "metadata");
-      this.metadata = ImmutableMap.copyOf(metadata);
-      return this;
-   }
-
-   /**
-    * An arbitrary set of tags can be set at provision time, but they must be prefixed with "tag."
-    */
-   public CreateServerOptions tag(Map<String, String> tag) {
-      checkNotNull(tag, "tag");
-      this.tag = ImmutableMap.copyOf(tag);
-      return this;
-   }
-
-   @SuppressWarnings("unused")
-   private class MachineRequest {
-      final String name;
-      @SerializedName("package")
-      final String packageSDC;
-      final String dataset;
-      Map<String, String> metadata;
-      Map<String, String> tag;
-
-      private MachineRequest(String name, String packageSDC, String dataset) {
-         this.name = name;
-         this.packageSDC = packageSDC;
-         this.dataset = dataset;
-      }
-
-   }
-
-   public static class Builder {
-
-      /**
-       * @see CreateServerOptions#metadata(Map<String, String>)
-       */
-      public static CreateServerOptions metadata(Map<String, String> metadata) {
-         CreateServerOptions options = new CreateServerOptions();
-         return options.metadata(metadata);
-      }
-
-      /**
-       * @see CreateServerOptions#tag(Map<String, String>)
-       */
-      public static CreateServerOptions tag(Map<String, String> tag) {
-         CreateServerOptions options = new CreateServerOptions();
-         return options.tag(tag);
-      }
-   }
-
-}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/predicates/KeyPredicates.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/predicates/KeyPredicates.java
new file mode 100644
index 0000000..c5963b1
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/predicates/KeyPredicates.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.joyent.sdc.v6_5.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+
+import com.google.common.base.Predicate;
+
+/**
+ * Predicates handy when working with Keys
+ * 
+ * @author Adrian Cole
+ */
+
+public class KeyPredicates {
+
+
+   /**
+    * matches name of the given key pair
+    * 
+    * @param name
+    * @return predicate that matches name
+    */
+   public static Predicate<Key> nameMatches(final Predicate<String> name) {
+      checkNotNull(name, "name must be defined");
+
+      return new Predicate<Key>() {
+         @Override
+         public boolean apply(Key ext) {
+            return name.apply(ext.getName());
+         }
+
+         @Override
+         public String toString() {
+            return "nameMatches(" + name + ")";
+         }
+      };
+   }
+  
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/reference/MetadataKeys.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/reference/MetadataKeys.java
new file mode 100644
index 0000000..1d3a657
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/reference/MetadataKeys.java
@@ -0,0 +1,67 @@
+/**
+ * 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.joyent.sdc.v6_5.reference;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Known keys for metadata
+ * 
+ * @author Adrian Cole
+ */
+public enum MetadataKeys {
+   ROOT_AUTHORIZED_KEYS(TypeToken.of(String.class).getType()),
+   /**
+    * If the dataset you create a machine from is set to generate passwords for
+    * you, the username/password pairs will be returned in the metadata response
+    * as a nested object, like:
+    * 
+    * <pre>
+    * "credentials": {
+    *     "root": "s8v9kuht5e",
+    *     "admin": "mf4bteqhpy"
+    *   }
+    * </pre>
+    */
+   CREDENTIALS(new TypeToken<Map<String, String>>() {
+      private static final long serialVersionUID = -433136967305618708L;
+   }.getType());
+
+   private final Type valueType;
+
+   MetadataKeys(Type valueType) {
+      this.valueType = valueType;
+   }
+
+   public String key() {
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_UNDERSCORE, name());
+   }
+
+   /**
+    * type of the value; 
+    */
+   public Type type() {
+      return valueType;
+   }
+
+}
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/suppliers/ZoneIdToURIFromDatacentersClient.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/suppliers/ZoneIdToURIFromDatacentersClient.java
new file mode 100644
index 0000000..06d14a1
--- /dev/null
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/suppliers/ZoneIdToURIFromDatacentersClient.java
@@ -0,0 +1,35 @@
+package org.jclouds.joyent.sdc.v6_5.suppliers;
+
+import java.net.URI;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.joyent.sdc.v6_5.features.DatacenterClient;
+import org.jclouds.location.suppliers.ZoneIdToURISupplier;
+import org.jclouds.util.Suppliers2;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.Maps;
+
+@Singleton
+public class ZoneIdToURIFromDatacentersClient implements ZoneIdToURISupplier {
+
+   private final DatacenterClient client;
+
+   @Inject
+   public ZoneIdToURIFromDatacentersClient(DatacenterClient client) {
+      this.client = client;
+   }
+
+   @Override
+   public Map<String, Supplier<URI>> get() {
+      return Maps.transformValues(client.getDatacenters(), Suppliers2.<URI> ofInstanceFunction());
+   }
+
+   @Override
+   public String toString() {
+      return "getDatacenters()";
+   }
+}
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/SDCClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/SDCClientExpectTest.java
new file mode 100644
index 0000000..901f943
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/SDCClientExpectTest.java
@@ -0,0 +1,47 @@
+/**
+ * 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
+ *
+ * Unles 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 expres or implied.  See the License for the
+ * specific language governing permisions and limitations
+ * under the License.
+ */
+package org.jclouds.joyent.sdc.v6_5;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "SDCClientExpectTest")
+public class SDCClientExpectTest extends BaseSDCClientExpectTest {
+
+   public void testGetConfiguredDatacenters() {
+
+      SDCClient clientWhenDatacentersExists = requestSendsResponse(getDatacenters, getDatacentersResponse);
+
+      assertEquals(
+            clientWhenDatacentersExists.getConfiguredDatacenters(),
+            ImmutableSet.<String> builder()
+                  .add("us-east-1")
+                  .add("us-west-1")
+                  .add("us-sw-1")
+                  .add("eu-ams-1").build());
+   }
+
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/SDCClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/SDCClientLiveTest.java
new file mode 100644
index 0000000..4060e79
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/SDCClientLiveTest.java
@@ -0,0 +1,38 @@
+/**
+ * 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.joyent.sdc.v6_5;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Set;
+
+import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "SDCClientLiveTest")
+public class SDCClientLiveTest extends BaseSDCClientLiveTest {
+
+   public void testGetDatacenters() {
+      Set<String> dcs = sdcContext.getApi().getConfiguredDatacenters();
+      assertEquals(dcs, sdcContext.getApi().getDatacenterClient().getDatacenters().keySet());
+   }
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceLiveTest.java
new file mode 100644
index 0000000..dac1eef
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/SDCComputeServiceLiveTest.java
@@ -0,0 +1,26 @@
+package org.jclouds.joyent.sdc.v6_5.compute;
+
+import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import com.google.inject.Module;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "SDCComputeServiceLiveTest")
+public class SDCComputeServiceLiveTest extends BaseComputeServiceLiveTest {
+
+   public SDCComputeServiceLiveTest() {
+      provider = "joyent-sdc";
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+   
+
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImageTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImageTest.java
new file mode 100644
index 0000000..c517630
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetInDatacenterToImageTest.java
@@ -0,0 +1,94 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.joyent.sdc.v6_5.domain.Dataset;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatasetInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.parse.ParseDatasetTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Tests the function that transforms sdc-specific images to generic images.
+ * 
+ * @author Adrian Cole
+ */
+@Test(testName = "DatasetInDatacenterToHardwareTest")
+public class DatasetInDatacenterToImageTest {
+
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-sdc")
+         .description("joyent-sdc").build();
+   Location zone = new LocationBuilder().id("us-sw-1").description("us-sw-1").scope(LocationScope.ZONE)
+         .parent(provider).build();
+   Supplier<Map<String, Location>> locationIndex = Suppliers.<Map<String, Location>> ofInstance(ImmutableMap
+         .<String, Location> of("us-sw-1", zone));
+
+   Dataset datasetToConvert = new ParseDatasetTest().expected();
+
+   @Test
+   public void testConversionWhereLocationFound() {
+      OperatingSystem operatingSystem = new OperatingSystem(OsFamily.UBUNTU, "My Test OS", "My Test Version", "x86",
+            "My Test OS", true);
+      DatasetInDatacenterToImage converter = new DatasetInDatacenterToImage(constant(operatingSystem), locationIndex);
+
+      DatasetInDatacenter datasetInZoneToConvert = new DatasetInDatacenter(datasetToConvert, "us-sw-1");
+
+      org.jclouds.compute.domain.Image convertedImage = converter.apply(datasetInZoneToConvert);
+
+      assertEquals(convertedImage.getId(), datasetInZoneToConvert.slashEncode());
+      assertEquals(convertedImage.getProviderId(), datasetToConvert.getId());
+      assertEquals(convertedImage.getLocation(), locationIndex.get().get("us-sw-1"));
+
+      assertEquals(convertedImage.getName(), datasetToConvert.getName());
+      assertEquals(convertedImage.getStatus(), org.jclouds.compute.domain.Image.Status.AVAILABLE);
+      assertEquals(convertedImage.getOperatingSystem(), operatingSystem);
+      assertEquals(convertedImage.getDescription(), datasetToConvert.getUrn());
+      assertEquals(convertedImage.getVersion(), datasetToConvert.getVersion());
+   }
+
+   @Test(expectedExceptions = IllegalStateException.class)
+   public void testConversionWhereLocationNotFound() {
+      OperatingSystem operatingSystem = new OperatingSystem(OsFamily.UBUNTU, "My Test OS", "My Test Version", "x86",
+            "My Test OS", true);
+      DatasetInDatacenterToImage converter = new DatasetInDatacenterToImage(constant(operatingSystem), locationIndex);
+
+      DatasetInDatacenter datasetInZoneToConvert = new DatasetInDatacenter(datasetToConvert, "South");
+
+      converter.apply(datasetInZoneToConvert);
+   }
+
+   @SuppressWarnings("unchecked")
+   private static Function<Dataset, OperatingSystem> constant(OperatingSystem operatingSystem) {
+      return Function.class.cast(Functions.constant(operatingSystem));
+   }
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetToOperatingSystemTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetToOperatingSystemTest.java
new file mode 100644
index 0000000..8c014a3
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/DatasetToOperatingSystemTest.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.joyent.sdc.v6_5.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.joyent.sdc.v6_5.domain.Dataset;
+import org.jclouds.joyent.sdc.v6_5.parse.ParseDatasetTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Tests for the function for transforming a sdc specific Image into a generic
+ * OperatingSystem object.
+ * 
+ * @author Adrian Cole
+ */
+@Test(testName = "DatasetToOperatingSystemTest")
+public class DatasetToOperatingSystemTest {
+
+   public void testCentos6() {
+
+      Dataset datasetToConvert = new ParseDatasetTest().expected();
+
+      OperatingSystem convertedOs = new DatasetToOperatingSystem(ImmutableMap.<OsFamily, Map<String, String>> of(
+            OsFamily.CENTOS, ImmutableMap.of("6", "6.0"))).apply(datasetToConvert);
+
+      assertEquals(convertedOs.getName(), datasetToConvert.getName());
+      assertEquals(convertedOs.getFamily(), OsFamily.CENTOS);
+      assertEquals(convertedOs.getDescription(), datasetToConvert.getUrn());
+      assertEquals(convertedOs.getVersion(), "6.0");
+      assertEquals(convertedOs.getArch(), null);
+      assertTrue(convertedOs.is64Bit());
+   }
+
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadataTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadataTest.java
new file mode 100644
index 0000000..2ff1ac0
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/MachineInDatacenterToNodeMetadataTest.java
@@ -0,0 +1,142 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.internal.VolumeImpl;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.joyent.sdc.v6_5.compute.config.SDCComputeServiceContextModule;
+import org.jclouds.joyent.sdc.v6_5.domain.Machine;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.MachineInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.parse.ParseCreatedMachineTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+
+/**
+ * Tests for the function for transforming a sdc specific Machine into a generic
+ * NodeMetadata object.
+ * 
+ * @author Adrian Cole
+ */
+@Test(testName = "MachineInDatacenterToNodeMetadataTest")
+public class MachineInDatacenterToNodeMetadataTest {
+
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-sdc")
+         .description("joyent-sdc").build();
+   Location zone = new LocationBuilder().id("us-sw-1").description("us-sw-1").scope(LocationScope.ZONE)
+         .parent(provider).build();
+   Supplier<Map<String, Location>> locationIndex = Suppliers.<Map<String, Location>> ofInstance(ImmutableMap
+         .<String, Location> of("us-sw-1", zone));
+
+   GroupNamingConvention.Factory namingConvention = Guice.createInjector().getInstance(
+         GroupNamingConvention.Factory.class);
+
+   @Test
+   public void testWhenNoHardwareOrImageMatchImageIdIsStillSet() {
+
+      Hardware existingHardware = new HardwareBuilder().id("us-sw-1/FOOOOOOOO").providerId("FOOOOOOOO").location(zone)
+            .build();
+      Image existingImage = new ImageBuilder().id("us-sw-1/FOOOOOOOO")
+            .operatingSystem(OperatingSystem.builder().family(OsFamily.LINUX).description("foobuntu").build())
+            .providerId("FOOOOOOOO").description("foobuntu").location(zone).status(Image.Status.AVAILABLE).build();
+
+      checkHardwareAndImageStatus(null, existingHardware, "us-sw-1/sdc:sdc:centos-5.7:1.2.1", null, existingImage);
+   }
+
+   @Test
+   public void testWhenHardwareAndImageMatchHardwareOperatingSystemAndImageIdAreSet() {
+
+      Hardware existingHardware = new HardwareBuilder().id("us-sw-1/Small 1GB").providerId("Small 1GB").ram(1024)
+            .volume(new VolumeImpl(Float.valueOf(61440), true, true)).location(zone).build();
+      Image existingImage = new ImageBuilder().id("us-sw-1/sdc:sdc:centos-5.7:1.2.1")
+            .operatingSystem(OperatingSystem.builder().family(OsFamily.LINUX).description("foobuntu").build())
+            .providerId("sdc:sdc:centos-5.7:1.2.1").description("foobuntu").status(Image.Status.AVAILABLE)
+            .location(zone).build();
+
+      checkHardwareAndImageStatus(existingHardware, existingHardware, existingImage.getId(),
+            existingImage.getOperatingSystem(), existingImage);
+   }
+
+   // TODO: clean up this syntax
+   private void checkHardwareAndImageStatus(Hardware expectedHardware, Hardware existingHardware,
+         String expectedImageId, OperatingSystem expectedOs, Image existingImage) {
+
+      Set<Image> images = existingImage == null ? ImmutableSet.<Image> of() : ImmutableSet.of(existingImage);
+      Set<Hardware> hardwares = existingHardware == null ? ImmutableSet.<Hardware> of() : ImmutableSet
+            .of(existingHardware);
+      Machine machineToConvert = new ParseCreatedMachineTest().expected();
+
+      MachineInDatacenter machineInDatacenterToConvert = new MachineInDatacenter(machineToConvert, "us-sw-1");
+
+      MachineInDatacenterToNodeMetadata converter = new MachineInDatacenterToNodeMetadata(
+            SDCComputeServiceContextModule.toPortableNodeStatus, locationIndex,
+            Suppliers.<Set<? extends Image>> ofInstance(images),
+            Suppliers.<Set<? extends Hardware>> ofInstance(hardwares), namingConvention);
+
+      NodeMetadata convertedNodeMetadata = converter.apply(machineInDatacenterToConvert);
+
+      assertEquals(machineInDatacenterToConvert.slashEncode(), convertedNodeMetadata.getId());
+      assertEquals(machineToConvert.getId(), convertedNodeMetadata.getProviderId());
+
+      assertEquals(convertedNodeMetadata.getLocation().getScope(), LocationScope.ZONE);
+      assertEquals(convertedNodeMetadata.getLocation().getId(), "us-sw-1");
+
+      assertEquals(machineToConvert.getName(), convertedNodeMetadata.getName());
+      assertEquals(convertedNodeMetadata.getGroup(), "sample");
+
+      assertEquals(convertedNodeMetadata.getImageId(), expectedImageId);
+      assertEquals(convertedNodeMetadata.getOperatingSystem(), expectedOs);
+
+      assertEquals(convertedNodeMetadata.getHardware(), expectedHardware);
+
+      assertEquals(SDCComputeServiceContextModule.toPortableNodeStatus.get(machineToConvert.getState()),
+            convertedNodeMetadata.getStatus());
+
+      assertNotNull(convertedNodeMetadata.getPrivateAddresses());
+      assertEquals(convertedNodeMetadata.getPrivateAddresses(), ImmutableSet.of("10.224.0.63"));
+
+      assertNotNull(convertedNodeMetadata.getPublicAddresses());
+      assertEquals(convertedNodeMetadata.getPublicAddresses(), ImmutableSet.of("37.153.96.62"));
+
+      assertNotNull(convertedNodeMetadata.getUserMetadata());
+      // ensure filtered out root_authorized_keys!
+      assertEquals(convertedNodeMetadata.getUserMetadata(), ImmutableMap.<String, String> of());
+   }
+
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterIdTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterIdTest.java
new file mode 100644
index 0000000..0d61775
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/OrphanedGroupsByDatacenterIdTest.java
@@ -0,0 +1,100 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.joyent.sdc.v6_5.compute.config.SDCComputeServiceContextModule;
+import org.jclouds.joyent.sdc.v6_5.domain.Machine.State;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.MachineInDatacenter;
+import org.jclouds.joyent.sdc.v6_5.parse.ParseMachineTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+
+/**
+ * 
+ * 
+ * @author Adrian Cole
+ */
+@Test(testName = "OrphanedGroupsByDatacenterIdTest")
+public class OrphanedGroupsByDatacenterIdTest {
+
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-sdc").description(
+            "joyent-sdc").build();
+   Location datacenter = new LocationBuilder().id("us-east-1").description("us-east-1").scope(
+            LocationScope.ZONE).parent(provider).build();
+   Supplier<Map<String, Location>> locationIndex = Suppliers.<Map<String, Location>> ofInstance(ImmutableMap
+            .<String, Location> of("us-east-1", datacenter));
+
+   GroupNamingConvention.Factory namingConvention = Guice.createInjector().getInstance(GroupNamingConvention.Factory.class);
+
+   MachineInDatacenter machine1 = new MachineInDatacenter(new ParseMachineTest().expected().toBuilder().name("test-fe2").state(State.DELETED).build(), "us-east-1");
+   MachineInDatacenter machine2 = new MachineInDatacenter(new ParseMachineTest().expected().toBuilder().name("sample-fe1").state(State.DELETED).build(), "us-east-1");
+   
+   @Test
+   public void testWhenComputeServiceSaysAllNodesAreDeadBothGroupsAreReturned() {
+
+      
+      MachineInDatacenterToNodeMetadata converter = new MachineInDatacenterToNodeMetadata(
+               SDCComputeServiceContextModule.toPortableNodeStatus, locationIndex, Suppliers
+               .<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of()), Suppliers
+               .<Set<? extends Hardware>> ofInstance(ImmutableSet.<Hardware> of()), namingConvention);
+
+      Set<? extends NodeMetadata> set = ImmutableSet.of(converter.apply(machine2), converter.apply(machine1));
+
+      assertEquals(new OrphanedGroupsByDatacenterId(Predicates.<DatacenterAndName> alwaysTrue()).apply(set), ImmutableMultimap
+               .<String, String> builder().putAll("us-east-1", "sample", "test").build());
+   }
+
+   @Test
+   public void testWhenComputeServiceSaysAllNodesAreDeadNoGroupsAreReturned() {
+
+      MachineInDatacenter machine1 = new MachineInDatacenter(new ParseMachineTest().expected(), "us-east-1");
+      MachineInDatacenter machine2 = new MachineInDatacenter(new ParseMachineTest().expected(), "us-east-1");
+
+      MachineInDatacenterToNodeMetadata converter = new MachineInDatacenterToNodeMetadata(
+               SDCComputeServiceContextModule.toPortableNodeStatus, locationIndex, Suppliers
+                        .<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of()), Suppliers
+                        .<Set<? extends Hardware>> ofInstance(ImmutableSet.<Hardware> of()), namingConvention);
+
+      Set<? extends NodeMetadata> set = ImmutableSet.of(converter.apply(machine2), converter.apply(machine1));
+
+      assertEquals(new OrphanedGroupsByDatacenterId(Predicates.<DatacenterAndName> alwaysFalse()).apply(set), ImmutableMultimap
+               .<String, String> of());
+
+   }
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardwareTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardwareTest.java
new file mode 100644
index 0000000..8a88530
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/functions/PackageInDatacenterToHardwareTest.java
@@ -0,0 +1,88 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.Map;
+
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.joyent.sdc.v6_5.domain.Package;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.PackageInDatacenter;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Tests the function used to transform Package objects into Hardware objects
+ * 
+ * @author Adrian Cole
+ */
+@Test(testName = "PackageInDatacenterToHardwareTest")
+public class PackageInDatacenterToHardwareTest {
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("joyent-sdc").description(
+            "joyent-sdc").build();
+   Location zone = new LocationBuilder().id("us-sw-1").description("us-sw-1").scope(
+            LocationScope.ZONE).parent(provider).build();
+   Supplier<Map<String, Location>> locationIndex = Suppliers.<Map<String, Location>> ofInstance(ImmutableMap
+            .<String, Location> of("us-sw-1", zone));
+   
+   Package packageToConvert = org.jclouds.joyent.sdc.v6_5.domain.Package.builder().name("Small 1GB").memorySizeMb(1024)
+         .diskSizeGb(30720).swapSizeMb(2048).isDefault(true).build();
+
+   @Test
+   public void testConversionWhereLocationFound() {
+
+      PackageInDatacenter packageInZoneToConvert = new PackageInDatacenter(packageToConvert, "us-sw-1");
+
+      Hardware converted = new PackageInDatacenterToHardware(locationIndex).apply(packageInZoneToConvert);
+
+      assertEquals(converted.getName(), packageToConvert.getName());
+      assertEquals(converted.getId(), packageInZoneToConvert.slashEncode());
+      assertEquals(converted.getProviderId(), packageToConvert.getName());
+      assertEquals(converted.getLocation(), locationIndex.get().get("us-sw-1"));
+
+      assertEquals(converted.getRam(), packageToConvert.getMemorySizeMb());
+
+//TODO!
+//      assertNotNull(converted.getProcessors());
+//      assertFalse(converted.getProcessors().isEmpty());
+//      assertEquals(converted.getProcessors().iterator().next().getCores(), (double) packageToConvert.getVcpus());
+
+      assertNotNull(converted.getVolumes());
+      assertFalse(converted.getVolumes().isEmpty());
+      assertEquals(converted.getVolumes().iterator().next().getSize(), Float.valueOf(packageToConvert.getDiskSizeGb()));
+
+   }
+
+   @Test(expectedExceptions = IllegalStateException.class)
+   public void testConversionWhereLocationNotFound() {
+
+      PackageInDatacenter packageInZoneToConvert = new PackageInDatacenter(packageToConvert, "South");
+      new PackageInDatacenterToHardware(locationIndex).apply(packageInZoneToConvert);
+   }
+
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKeyTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKeyTest.java
new file mode 100644
index 0000000..9e2a4a2
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/loaders/CreateUniqueKeyTest.java
@@ -0,0 +1,118 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.loaders;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.jclouds.crypto.PemsTest.PRIVATE_KEY;
+import static org.jclouds.crypto.PemsTest.PUBLIC_KEY;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.functions.GroupNamingConvention.Factory;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.crypto.Pems;
+import org.jclouds.crypto.SshKeys;
+import org.jclouds.io.Payloads;
+import org.jclouds.joyent.sdc.v6_5.SDCClient;
+import org.jclouds.joyent.sdc.v6_5.compute.internal.KeyAndPrivateKey;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.DatacenterAndName;
+import org.jclouds.joyent.sdc.v6_5.features.KeyClient;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.TypeLiteral;
+import com.google.inject.util.Providers;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "CreateUniqueKeyTest")
+public class CreateUniqueKeyTest {
+   private Factory namingConvention;
+   private KeyPair keyPair;
+   private String openSshKey;
+
+   @BeforeClass
+   public void setup() throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
+      namingConvention = Guice.createInjector(new AbstractModule() {
+
+         @Override
+         protected void configure() {
+            bind(new TypeLiteral<Supplier<String>>() {
+            }).toInstance(Suppliers.ofInstance("foo"));
+         }
+      }).getInstance(GroupNamingConvention.Factory.class);
+      KeyFactory keyfactory = KeyFactory.getInstance("RSA");
+      PrivateKey privateKey = keyfactory.generatePrivate(Pems.privateKeySpec(Payloads.newStringPayload(PRIVATE_KEY)));
+
+      PublicKey publicKey = keyfactory
+            .generatePublic(Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY)));
+
+      keyPair = new KeyPair(publicKey, privateKey);
+      openSshKey = SshKeys.encodeAsOpenSSH(RSAPublicKey.class.cast(publicKey));
+   }
+
+   @Test
+   public void testApply() {
+      SDCClient sdcClient = createMock(SDCClient.class);
+      KeyClient keyClient = createMock(KeyClient.class);
+      Crypto crypto = createMock(Crypto.class);
+      KeyPairGenerator rsaKeyPairGenerator = createMock(KeyPairGenerator.class);
+      SecureRandom secureRandom = createMock(SecureRandom.class);
+
+      Key key = Key.builder().name("group-foo").key(openSshKey).build();
+
+      expect(crypto.rsaKeyPairGenerator()).andReturn(rsaKeyPairGenerator);
+      rsaKeyPairGenerator.initialize(2048, secureRandom);
+      expect(rsaKeyPairGenerator.genKeyPair()).andReturn(keyPair);
+
+      expect(sdcClient.getKeyClient()).andReturn(keyClient);
+
+      expect(keyClient.create(key)).andReturn(key);
+
+      replay(sdcClient, keyClient, crypto, rsaKeyPairGenerator, secureRandom);
+
+      CreateUniqueKey parser = new CreateUniqueKey(sdcClient, namingConvention, crypto, Providers.of(secureRandom));
+
+      assertEquals(parser.load(DatacenterAndName.fromDatacenterAndName("datacenter", "group")),
+            KeyAndPrivateKey.fromKeyAndPrivateKey(key, PRIVATE_KEY));
+
+      verify(sdcClient, keyClient, crypto, rsaKeyPairGenerator, secureRandom);
+   }
+
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptionsTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptionsTest.java
new file mode 100644
index 0000000..bf75a90
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/compute/options/SDCTemplateOptionsTest.java
@@ -0,0 +1,181 @@
+/**
+ * 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.joyent.sdc.v6_5.compute.options;
+
+import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.authorizePublicKey;
+import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.blockOnPort;
+import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.generateKey;
+import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.inboundPorts;
+import static org.jclouds.joyent.sdc.v6_5.compute.options.SDCTemplateOptions.Builder.installPrivateKey;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.jclouds.compute.options.TemplateOptions;
+import org.testng.annotations.Test;
+
+/**
+ * Tests possible uses of SDCTemplateOptions and SDCTemplateOptions.Builder.*
+ * 
+ * @author Adrian Cole
+ */
+@Test(testName = "SDCTemplateOptionsTest")
+public class SDCTemplateOptionsTest {
+
+   public void testAs() {
+      TemplateOptions options = new SDCTemplateOptions();
+      assertEquals(options.as(SDCTemplateOptions.class), options);
+   }
+
+   @Test
+   public void testGenerateKeyDefault() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      assert !options.shouldGenerateKey();
+   }
+
+   @Test
+   public void testGenerateKey() {
+      SDCTemplateOptions options = new SDCTemplateOptions().generateKey(true);
+      assert options.shouldGenerateKey();
+   }
+
+   @Test
+   public void testGenerateKeyStatic() {
+      SDCTemplateOptions options = generateKey(true);
+      assert options.shouldGenerateKey();
+   }
+
+   // superclass tests
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testinstallPrivateKeyBadFormat() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      options.installPrivateKey("whompy");
+   }
+
+   @Test
+   public void testinstallPrivateKey() throws IOException {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      options.installPrivateKey("-----BEGIN RSA PRIVATE KEY-----");
+      assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----");
+   }
+
+   @Test
+   public void testNullinstallPrivateKey() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      assertEquals(options.getPrivateKey(), null);
+   }
+
+   @Test
+   public void testinstallPrivateKeyStatic() throws IOException {
+      SDCTemplateOptions options = installPrivateKey("-----BEGIN RSA PRIVATE KEY-----");
+      assertEquals(options.getPrivateKey(), "-----BEGIN RSA PRIVATE KEY-----");
+   }
+
+   @Test(expectedExceptions = NullPointerException.class)
+   public void testinstallPrivateKeyNPE() {
+      installPrivateKey(null);
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testauthorizePublicKeyBadFormat() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      options.authorizePublicKey("whompy");
+   }
+
+   @Test
+   public void testauthorizePublicKey() throws IOException {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      options.authorizePublicKey("ssh-rsa");
+      assertEquals(options.getPublicKey(), "ssh-rsa");
+   }
+
+   @Test
+   public void testNullauthorizePublicKey() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      assertEquals(options.getPublicKey(), null);
+   }
+
+   @Test
+   public void testauthorizePublicKeyStatic() throws IOException {
+      SDCTemplateOptions options = authorizePublicKey("ssh-rsa");
+      assertEquals(options.getPublicKey(), "ssh-rsa");
+   }
+
+   @Test(expectedExceptions = NullPointerException.class)
+   public void testauthorizePublicKeyNPE() {
+      authorizePublicKey(null);
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testblockOnPortBadFormat() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      options.blockOnPort(-1, -1);
+   }
+
+   @Test
+   public void testblockOnPort() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      options.blockOnPort(22, 30);
+      assertEquals(options.getPort(), 22);
+      assertEquals(options.getSeconds(), 30);
+
+   }
+
+   @Test
+   public void testNullblockOnPort() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      assertEquals(options.getPort(), -1);
+      assertEquals(options.getSeconds(), -1);
+   }
+
+   @Test
+   public void testblockOnPortStatic() {
+      SDCTemplateOptions options = blockOnPort(22, 30);
+      assertEquals(options.getPort(), 22);
+      assertEquals(options.getSeconds(), 30);
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testinboundPortsBadFormat() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      options.inboundPorts(-1, -1);
+   }
+
+   @Test
+   public void testinboundPorts() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      options.inboundPorts(22, 30);
+      assertEquals(options.getInboundPorts()[0], 22);
+      assertEquals(options.getInboundPorts()[1], 30);
+
+   }
+
+   @Test
+   public void testDefaultOpen22() {
+      SDCTemplateOptions options = new SDCTemplateOptions();
+      assertEquals(options.getInboundPorts()[0], 22);
+   }
+
+   @Test
+   public void testinboundPortsStatic() {
+      SDCTemplateOptions options = inboundPorts(22, 30);
+      assertEquals(options.getInboundPorts()[0], 22);
+      assertEquals(options.getInboundPorts()[1], 30);
+   }
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientExpectTest.java
index 07a6623..2dc69ab 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientExpectTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatacenterClientExpectTest.java
@@ -22,32 +22,20 @@
 
 import java.net.URI;
 
-import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.joyent.sdc.v6_5.SDCClient;
 import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientExpectTest;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
 
 /**
  * @author Adrian Cole
  */
 @Test(groups = "unit", testName = "DatacenterClientExpectTest")
 public class DatacenterClientExpectTest extends BaseSDCClientExpectTest {
-   HttpRequest getDatacenters = HttpRequest
-         .builder()
-         .method("GET")
-         .endpoint(URI.create("https://api.joyentcloud.com/my/datacenters"))
-         .headers(
-               ImmutableMultimap.<String, String> builder().put("X-Api-Version", "~6.5")
-                     .put("Accept", "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==")
-                     .build()).build();
 
    public void testGetDatacentersWhenResponseIs2xx() {
-      HttpResponse getDatacentersResponse = HttpResponse.builder().statusCode(200)
-            .payload(payloadFromResource("/datacenters.json")).build();
 
       SDCClient clientWhenDatacentersExists = requestSendsResponse(getDatacenters, getDatacentersResponse);
 
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java
index 20575f1..8e0e951 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientExpectTest.java
@@ -37,27 +37,27 @@
  */
 @Test(groups = "unit", testName = "DatasetClientExpectTest")
 public class DatasetClientExpectTest extends BaseSDCClientExpectTest {
-   HttpRequest listDatasets = HttpRequest.builder().method("GET").endpoint(
-            URI.create("https://api.joyentcloud.com/my/datasets")).headers(
+   HttpRequest list = HttpRequest.builder().method("GET").endpoint(
+            URI.create("https://us-sw-1.api.joyentcloud.com/my/datasets")).headers(
             ImmutableMultimap.<String, String> builder().put("X-Api-Version", "~6.5").put("Accept", "application/json")
                      .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build();
 
    public void testListDatasetsWhenResponseIs2xx() {
-      HttpResponse listDatasetsResponse = HttpResponse.builder().statusCode(200).payload(
+      HttpResponse listResponse = HttpResponse.builder().statusCode(200).payload(
                payloadFromResource("/dataset_list.json")).build();
 
-      SDCClient clientWhenDatasetsExists = requestSendsResponse(listDatasets, listDatasetsResponse);
+      SDCClient clientWhenDatasetsExists = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
 
-      assertEquals(clientWhenDatasetsExists.getDatasetClient().listDatasets().toString(), new ParseDatasetListTest()
+      assertEquals(clientWhenDatasetsExists.getDatasetClientForDatacenter("us-sw-1").list().toString(), new ParseDatasetListTest()
                .expected().toString());
    }
 
    public void testListDatasetsWhenResponseIs404() {
-      HttpResponse listDatasetsResponse = HttpResponse.builder().statusCode(404).build();
+      HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
 
-      SDCClient listDatasetsWhenNone = requestSendsResponse(listDatasets, listDatasetsResponse);
+      SDCClient listWhenNone = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
 
-      assertEquals(listDatasetsWhenNone.getDatasetClient().listDatasets(), ImmutableSet.of());
+      assertEquals(listWhenNone.getDatasetClientForDatacenter("us-sw-1").list(), ImmutableSet.of());
    }
 
    // [id=e4cd7b9e-4330-11e1-81cf-3bb50a972bda, name=centos-6, type=VIRTUALMACHINE, version=1.0.1,
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java
index 31bf750..0d62c86 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/DatasetClientLiveTest.java
@@ -18,9 +18,7 @@
  */
 package org.jclouds.joyent.sdc.v6_5.features;
 
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.Assert.assertEquals;
 
 import java.util.Set;
 
@@ -34,16 +32,22 @@
 @Test(groups = "live", testName = "DatasetClientLiveTest")
 public class DatasetClientLiveTest extends BaseSDCClientLiveTest {
 
-   public void testListDatasets() {
-      Set<Dataset> datasets = sdcContext.getApi().getDatasetClient().listDatasets();
-      assertNotNull(datasets);
-      assertTrue(datasets.size() > 0);
-   }
-
-   public void testGetDataset() {
-      final String id = "e4cd7b9e-4330-11e1-81cf-3bb50a972bda";
-      Dataset dataset = sdcContext.getApi().getDatasetClient().getDataset(id);
-      assertNotNull(dataset);
-      assertEquals(dataset.getId(), id);
+   @Test
+   public void testListAndGetDatasets() throws Exception {
+      for (String datacenterId : sdcContext.getApi().getConfiguredDatacenters()) {
+         DatasetClient client = sdcContext.getApi().getDatasetClientForDatacenter(datacenterId);
+         Set<Dataset> response = client.list();
+         assert null != response;
+         for (Dataset dataset : response) {
+            Dataset newDetails = client.get(dataset.getId());
+            assertEquals(newDetails.getId(), dataset.getId());
+            assertEquals(newDetails.getName(), dataset.getName());
+            assertEquals(newDetails.getType(), dataset.getType());
+            assertEquals(newDetails.getVersion(), dataset.getVersion());
+            assertEquals(newDetails.getUrn(), dataset.getUrn());
+            assertEquals(newDetails.getCreated(), dataset.getCreated());
+            assertEquals(newDetails.isDefault(), dataset.isDefault());
+         }
+      }
    }
 }
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/KeyClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/KeyClientExpectTest.java
new file mode 100644
index 0000000..e8190c8
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/KeyClientExpectTest.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
+ *
+ * Unles 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 expres or implied.  See the License for the
+ * specific language governing permisions and limitations
+ * under the License.
+ */
+package org.jclouds.joyent.sdc.v6_5.features;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.joyent.sdc.v6_5.SDCClient;
+import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientExpectTest;
+import org.jclouds.joyent.sdc.v6_5.parse.ParseKeyListTest;
+import org.jclouds.joyent.sdc.v6_5.parse.ParseKeyTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "KeyClientExpectTest")
+public class KeyClientExpectTest extends BaseSDCClientExpectTest {
+   HttpRequest list = HttpRequest.builder().method("GET").endpoint(
+            URI.create("https://api.joyentcloud.com/my/keys")).headers(
+            ImmutableMultimap.<String, String> builder().put("X-Api-Version", "~6.5").put("Accept", "application/json")
+                     .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build();
+
+   public void testListKeysWhenResponseIs2xx() {
+      HttpResponse listResponse = HttpResponse.builder().statusCode(200).payload(
+               payloadFromResource("/key_list.json")).build();
+
+      SDCClient clientWhenKeysExists = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
+
+      assertEquals(clientWhenKeysExists.getKeyClient().list(), new ParseKeyListTest().expected());
+   }
+
+   public void testListKeysWhenResponseIs404() {
+      HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
+
+      SDCClient listWhenNone = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
+
+      assertEquals(listWhenNone.getKeyClient().list(), ImmutableSet.of());
+   }
+
+   public void testCreateKeyWhenResponseIs202() throws Exception {
+      HttpRequest create = HttpRequest
+               .builder()
+               .method("POST")
+               .endpoint(URI.create("https://api.joyentcloud.com/my/keys"))
+               .headers(
+                        ImmutableMultimap.<String, String> builder().put("X-Api-Version", "~6.5").put("Accept",
+                                 "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+               .payload(
+                     payloadFromStringWithContentType(
+                           "{\"name\":\"rsa\",\"key\":\"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0A5Pf5Cq...\"}",
+                           "application/json")).build();
+
+      HttpResponse createResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
+               .payload(payloadFromResourceWithContentType("/key.json", "application/json; charset=UTF-8"))
+               .build();
+
+      SDCClient clientWithNewKey = requestsSendResponses(getDatacenters, getDatacentersResponse, create, createResponse);
+
+      assertEquals(clientWithNewKey.getKeyClient().create(new ParseKeyTest().expected())
+            .toString(), new ParseKeyTest().expected().toString());
+   }
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/KeyClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/KeyClientLiveTest.java
new file mode 100644
index 0000000..072b1a5
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/KeyClientLiveTest.java
@@ -0,0 +1,86 @@
+/**
+ * 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.joyent.sdc.v6_5.features;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Set;
+
+import org.jclouds.crypto.SshKeys;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientLiveTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "KeyClientLiveTest")
+public class KeyClientLiveTest extends BaseSDCClientLiveTest {
+
+   @Test
+   public void testListAndGetKeys() throws Exception {
+      KeyClient client = sdcContext.getApi().getKeyClient();
+      Set<Key> response = client.list();
+      assert null != response;
+      for (Key key : response) {
+         Key newDetails = client.get(key.getName());
+         assertEquals(newDetails.getName(), key.getName());
+         assertEquals(newDetails.get(), key.get());
+         assertEquals(newDetails.getCreated(), key.getCreated());
+      }
+
+   }
+   
+   private String keyText;
+   private String fingerprint;
+
+   @BeforeTest
+   public void initKeys() {
+      keyText = SshKeys.generate().get("public");
+      fingerprint = SshKeys.fingerprintPublicKey(keyText);
+   }
+
+   public void testCreateKey() {
+      KeyClient client = sdcContext.getApi().getKeyClient();
+
+      Key newKey = client.create(Key.builder().name(fingerprint).key(keyText).build());
+      assertEquals(newKey.getName(), fingerprint);
+      assertEquals(newKey.get(), keyText);
+
+      newKey = client.get(fingerprint);
+      assertEquals(newKey.getName(), fingerprint);
+      assertEquals(newKey.get(), keyText);
+   }
+
+   @Test(dependsOnMethods = "testCreateKey", expectedExceptions = IllegalStateException.class)
+   public void testDuplicateKey() {
+      KeyClient client = sdcContext.getApi().getKeyClient();
+      client.create(Key.builder().name(fingerprint).key(keyText).build());
+   }
+
+   @Test(dependsOnMethods = "testDuplicateKey")
+   public void testDestroyKey() {
+      final KeyClient client = sdcContext.getApi().getKeyClient();
+      client.delete(fingerprint);
+      // not that eventhough the key is destroyed it is visible via GET for at
+      // least 45 seconds. This may be a cache issue on the server
+   }
+
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java
index ef7d79f..78b658c 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientExpectTest.java
@@ -26,6 +26,7 @@
 import org.jclouds.http.HttpResponse;
 import org.jclouds.joyent.sdc.v6_5.SDCClient;
 import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientExpectTest;
+import org.jclouds.joyent.sdc.v6_5.options.CreateMachineOptions;
 import org.jclouds.joyent.sdc.v6_5.parse.ParseCreatedMachineTest;
 import org.jclouds.joyent.sdc.v6_5.parse.ParseMachineListTest;
 import org.testng.annotations.Test;
@@ -38,48 +39,50 @@
  */
 @Test(groups = "unit", testName = "MachineClientExpectTest")
 public class MachineClientExpectTest extends BaseSDCClientExpectTest {
-   HttpRequest listMachines = HttpRequest.builder().method("GET").endpoint(
-            URI.create("https://api.joyentcloud.com/my/machines")).headers(
+   HttpRequest list = HttpRequest.builder().method("GET").endpoint(
+            URI.create("https://us-sw-1.api.joyentcloud.com/my/machines")).headers(
             ImmutableMultimap.<String, String> builder().put("X-Api-Version", "~6.5").put("Accept", "application/json")
                      .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build();
 
    public void testListMachinesWhenResponseIs2xx() {
-      HttpResponse listMachinesResponse = HttpResponse.builder().statusCode(200).payload(
+      HttpResponse listResponse = HttpResponse.builder().statusCode(200).payload(
                payloadFromResource("/machine_list.json")).build();
 
-      SDCClient clientWhenMachinesExists = requestSendsResponse(listMachines, listMachinesResponse);
+      SDCClient clientWhenMachinesExists = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
 
-      assertEquals(clientWhenMachinesExists.getMachineClient().listMachines(), new ParseMachineListTest().expected());
+      assertEquals(clientWhenMachinesExists.getMachineClientForDatacenter("us-sw-1").list(), new ParseMachineListTest().expected());
    }
 
    public void testListMachinesWhenResponseIs404() {
-      HttpResponse listMachinesResponse = HttpResponse.builder().statusCode(404).build();
+      HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
 
-      SDCClient listMachinesWhenNone = requestSendsResponse(listMachines, listMachinesResponse);
+      SDCClient listWhenNone = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
 
-      assertEquals(listMachinesWhenNone.getMachineClient().listMachines(), ImmutableSet.of());
+      assertEquals(listWhenNone.getMachineClientForDatacenter("us-sw-1").list(), ImmutableSet.of());
    }
 
    public void testCreateMachineWhenResponseIs202() throws Exception {
-      HttpRequest createMachine = HttpRequest
+      HttpRequest createWithDataset = HttpRequest
                .builder()
                .method("POST")
-               .endpoint(URI.create("https://api.joyentcloud.com/my/machines"))
-               .headers(
-                        ImmutableMultimap.<String, String> builder().put("X-Api-Version", "~6.5").put("Accept",
-                                 "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-               .payload(
-                        payloadFromStringWithContentType(
-                                 "{\"name\":\"testJClouds\",\"package\":\"Small 1GB\",\"dataset\":\"sdc:sdc:centos-5.7:1.2.1\"}",
-                                 "application/json")).build();
+               .endpoint(URI.create("https://us-sw-1.api.joyentcloud.com/my/machines?dataset=sdc%3Asdc%3Acentos-5.7%3A1.2.1&name=sample-e92&package=Small%201GB"))
+               .headers(ImmutableMultimap.<String, String> builder()
+                        .put("X-Api-Version", "~6.5")
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+               .build();
 
-      HttpResponse createMachineResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
+      HttpResponse createWithDatasetResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
                .payload(payloadFromResourceWithContentType("/new_machine.json", "application/json; charset=UTF-8"))
                .build();
 
-      SDCClient clientWithNewMachine = requestSendsResponse(createMachine, createMachineResponse);
+      SDCClient clientWithNewMachine = requestsSendResponses(getDatacenters, getDatacentersResponse, createWithDataset, createWithDatasetResponse);
 
-      assertEquals(clientWithNewMachine.getMachineClient().createMachine("testJClouds", "Small 1GB",
-               "sdc:sdc:centos-5.7:1.2.1").toString(), new ParseCreatedMachineTest().expected().toString());
+      assertEquals(
+            clientWithNewMachine
+                  .getMachineClientForDatacenter("us-sw-1")
+                  .createWithDataset("sdc:sdc:centos-5.7:1.2.1",
+                        CreateMachineOptions.Builder.name("sample-e92").packageName("Small 1GB")).toString(),
+            new ParseCreatedMachineTest().expected().toString());
    }
 }
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java
index 7011ce5..25477e1 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/MachineClientLiveTest.java
@@ -18,32 +18,158 @@
  */
 package org.jclouds.joyent.sdc.v6_5.features;
 
+import static com.google.common.base.Predicates.not;
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.crypto.SshKeys;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
 import org.jclouds.joyent.sdc.v6_5.domain.Machine;
 import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientLiveTest;
+import org.jclouds.joyent.sdc.v6_5.options.CreateMachineOptions;
+import org.jclouds.joyent.sdc.v6_5.reference.MetadataKeys;
+import org.jclouds.predicates.InetSocketAddressConnect;
+import org.jclouds.predicates.RetryablePredicate;
+import org.jclouds.ssh.SshClient;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.jclouds.util.InetAddresses2;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.net.HostAndPort;
+import com.google.inject.Module;
+
 /**
- * @author Gerald Pereira
+ * @author Adrian Cole
  */
 @Test(groups = "live", testName = "MachineClientLiveTest")
 public class MachineClientLiveTest extends BaseSDCClientLiveTest {
 
-   public void testListMachines() {
-      Set<Machine> machines = sdcContext.getApi().getMachineClient().listMachines();
-      assertNotNull(machines);
-      assertTrue(machines.size() > 0);
+   @Test
+   public void testListAndGetMachines() throws Exception {
+      for (String datacenterId : sdcContext.getApi().getConfiguredDatacenters()) {
+         MachineClient client = sdcContext.getApi().getMachineClientForDatacenter(datacenterId);
+         Set<Machine> response = client.list();
+         assert null != response;
+         for (Machine machine : response) {
+            Machine newDetails = client.get(machine.getId());
+            assertEquals(newDetails.getId(), machine.getId());
+            assertEquals(newDetails.getName(), machine.getName());
+            assertEquals(newDetails.getType(), machine.getType());
+            assertEquals(newDetails.getState(), machine.getState());
+            assertEquals(newDetails.get(), machine.get());
+            assertEquals(newDetails.getMemorySizeMb(), machine.getMemorySizeMb());
+            assertEquals(newDetails.getDiskSizeGb(), machine.getDiskSizeGb());
+            assertEquals(newDetails.getIps(), machine.getIps());
+            assertEquals(newDetails.getCreated(), machine.getCreated());
+            assertEquals(newDetails.getUpdated(), machine.getUpdated());
+            assertEquals(newDetails.getMetadata(), machine.getMetadata());
+         }
+      }
    }
 
-   public void testGetMachine() {
-      final String id = "d73cb0b0-7d1f-44ef-8c40-e040eef0f726";
-      Machine machine = sdcContext.getApi().getMachineClient().getMachine(id);
-      assertNotNull(machine);
-      assertEquals(machine.getId(), id);
+   private Map<String, String> key;
+   private String fingerprint;
+   private RetryablePredicate<HostAndPort> socketTester;
+   private Predicate<Machine> machineRunning;
+   private MachineClient client;
+   private Machine machine;
+   protected String datasetURN = System.getProperty("test." + provider + ".image-id", "sdc:sdc:ubuntu-10.04:1.0.1");
+   private String name;
+
+   @BeforeGroups(groups = { "integration", "live" })
+   @Override
+   public void setupContext() {
+      super.setupContext();
+      key = SshKeys.generate();
+      fingerprint = SshKeys.fingerprintPublicKey(key.get("public"));
+      sdcContext.getApi().getKeyClient().create(Key.builder().name(fingerprint).key(key.get("public")).build());
+      client = sdcContext.getApi().getMachineClientForDatacenter(
+            Iterables.get(sdcContext.getApi().getConfiguredDatacenters(), 0));
+      socketTester = new RetryablePredicate<HostAndPort>(new InetSocketAddressConnect(), 180, 1, 1, TimeUnit.SECONDS);
+      machineRunning = new RetryablePredicate<Machine>(new Predicate<Machine>() {
+
+         @Override
+         public boolean apply(Machine input) {
+            return client.get(input.getId()).getState() == Machine.State.RUNNING;
+         }
+
+      }, 600, 5, 5, TimeUnit.SECONDS);
+      machineRunning = new RetryablePredicate<Machine>(new Predicate<Machine>() {
+
+         @Override
+         public boolean apply(Machine input) {
+            return client.get(input.getId()).getState() == Machine.State.RUNNING;
+         }
+
+      }, 600, 5, 5, TimeUnit.SECONDS);
+   }
+
+   public void testCreateMachine() {
+      Machine newMachine = client.createWithDataset(datasetURN,
+            CreateMachineOptions.Builder.metadata(ImmutableMap.of("foo", "bar")));
+      machine = newMachine;
+      name = newMachine.getName();
+
+      assertEquals(newMachine.getMetadata().get("foo").toString(), "bar");
+      assertTrue(
+            newMachine.getMetadata().get(MetadataKeys.ROOT_AUTHORIZED_KEYS.key()).indexOf(key.get("public")) != -1,
+            newMachine + "; key: " + key.get("public"));
+
+      assertTrue(machineRunning.apply(newMachine), newMachine.toString());
+      machine = client.get(newMachine.getId());
+
+   }
+
+   @Test(dependsOnMethods = "testCreateMachine", expectedExceptions = IllegalStateException.class)
+   public void testDuplicateMachineThrowsIllegalStateException() {
+      client.createWithDataset(datasetURN, CreateMachineOptions.Builder.name(name));
+   }
+
+   @Test(dependsOnMethods = "testCreateMachine")
+   protected void testSsh() {
+      String publicAddress = Iterables.find(machine.getIps(), not(InetAddresses2.IsPrivateIPAddress.INSTANCE));
+      HostAndPort socket = HostAndPort.fromParts(publicAddress, 22);
+      assertTrue(socketTester.apply(socket), socket.toString());
+      SshClient client = context.utils().injector().getInstance(SshClient.Factory.class)
+            .create(socket, LoginCredentials.builder().user("root").privateKey(key.get("private")).build());
+      try {
+         client.connect();
+         ExecResponse exec = client.exec("echo hello");
+         System.out.println(exec);
+         assertEquals(exec.getOutput().trim(), "hello");
+      } finally {
+         if (client != null)
+            client.disconnect();
+      }
+   }
+
+   @Test(dependsOnMethods = "testSsh")
+   public void testDeleteMachine() {
+      client.delete(machine.getId());
+   }
+
+   @Override
+   @AfterGroups(groups = "live")
+   protected void tearDown() {
+      if (machine != null)
+         client.delete(machine.getId());
+      sdcContext.getApi().getKeyClient().delete(fingerprint);
+      super.tearDown();
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
    }
 }
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java
index 9ecc673..86fbbd7 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientExpectTest.java
@@ -37,29 +37,29 @@
  */
 @Test(groups = "unit", testName = "PackageClientExpectTest")
 public class PackageClientExpectTest extends BaseSDCClientExpectTest {
-   HttpRequest listPackages = HttpRequest
+   HttpRequest list = HttpRequest
          .builder()
          .method("GET")
-         .endpoint(URI.create("https://api.joyentcloud.com/my/packages"))
+         .endpoint(URI.create("https://us-sw-1.api.joyentcloud.com/my/packages"))
          .headers(
                ImmutableMultimap.<String, String> builder().put("X-Api-Version", "~6.5")
                      .put("Accept", "application/json").put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==")
                      .build()).build();
 
    public void testListPackagesWhenResponseIs2xx() {
-      HttpResponse listPackagesResponse = HttpResponse.builder().statusCode(200)
+      HttpResponse listResponse = HttpResponse.builder().statusCode(200)
             .payload(payloadFromResource("/package_list.json")).build();
 
-      SDCClient clientWhenPackagesExists = requestSendsResponse(listPackages, listPackagesResponse);
+      SDCClient clientWhenPackagesExists = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
 
-      assertEquals(clientWhenPackagesExists.getPackageClient().listPackages(), new ParsePackageListTest().expected());
+      assertEquals(clientWhenPackagesExists.getPackageClientForDatacenter("us-sw-1").list(), new ParsePackageListTest().expected());
    }
 
    public void testListPackagesWhenResponseIs404() {
-      HttpResponse listPackagesResponse = HttpResponse.builder().statusCode(404).build();
+      HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
 
-      SDCClient listPackagesWhenNone = requestSendsResponse(listPackages, listPackagesResponse);
+      SDCClient listWhenNone = requestsSendResponses(getDatacenters, getDatacentersResponse, list, listResponse);
 
-      assertEquals(listPackagesWhenNone.getPackageClient().listPackages(), ImmutableSet.of());
+      assertEquals(listWhenNone.getPackageClientForDatacenter("us-sw-1").list(), ImmutableSet.of());
    }
 }
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java
index 55e1aaa..08fbd84 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/features/PackageClientLiveTest.java
@@ -18,12 +18,11 @@
  */
 package org.jclouds.joyent.sdc.v6_5.features;
 
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.Assert.assertEquals;
 
 import java.util.Set;
 
+import org.jclouds.joyent.sdc.v6_5.domain.Package;
 import org.jclouds.joyent.sdc.v6_5.internal.BaseSDCClientLiveTest;
 import org.testng.annotations.Test;
 
@@ -33,16 +32,20 @@
 @Test(groups = "live", testName = "PackageClientLiveTest")
 public class PackageClientLiveTest extends BaseSDCClientLiveTest {
 
-   public void testListPackages() {
-      Set<org.jclouds.joyent.sdc.v6_5.domain.Package> packages = sdcContext.getApi().getPackageClient().listPackages();
-      assertNotNull(packages);
-      assertTrue(packages.size() > 0);
-   }
-
-   public void testGetPackage() {
-      final String name = "Small 1GB";
-      org.jclouds.joyent.sdc.v6_5.domain.Package packageSDC = sdcContext.getApi().getPackageClient().getPackage(name);
-      assertNotNull(packageSDC);
-      assertEquals(packageSDC.getName(), name);
+   @Test
+   public void testListAndGetPackages() throws Exception {
+      for (String datacenterId : sdcContext.getApi().getConfiguredDatacenters()) {
+         PackageClient client = sdcContext.getApi().getPackageClientForDatacenter(datacenterId);
+         Set<Package> response = client.list();
+         assert null != response;
+         for (Package pkg : response) {
+            Package newDetails = client.get(pkg.getName());
+            assertEquals(newDetails.getName(), pkg.getName());
+            assertEquals(newDetails.getMemorySizeMb(), pkg.getMemorySizeMb());
+            assertEquals(newDetails.getDiskSizeGb(), pkg.getDiskSizeGb());
+            assertEquals(newDetails.getSwapSizeMb(), pkg.getSwapSizeMb());
+            assertEquals(newDetails.isDefault(), pkg.isDefault());
+         }
+      }
    }
 }
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientExpectTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientExpectTest.java
index cd342ce..bc1d4e2 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientExpectTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientExpectTest.java
@@ -18,13 +18,31 @@
  */
 package org.jclouds.joyent.sdc.v6_5.internal;
 
+import java.net.URI;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
 import org.jclouds.joyent.sdc.v6_5.SDCClient;
 
+import com.google.common.collect.ImmutableMultimap;
+
 /**
  * Base class for writing KeyStone Rest Client Expect tests
  * 
  * @author Adrian Cole
  */
 public class BaseSDCClientExpectTest extends BaseSDCExpectTest<SDCClient> {
-
+   protected HttpRequest getDatacenters = HttpRequest
+         .builder()
+         .method("GET")
+         .endpoint(URI.create("https://api.joyentcloud.com/my/datacenters"))
+         .headers(
+               ImmutableMultimap.<String, String> builder()
+                  .put("X-Api-Version", "~6.5")
+                  .put("Accept", "application/json")
+                  .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==")
+                  .build()).build();
+   
+   protected HttpResponse getDatacentersResponse = HttpResponse.builder().statusCode(200)
+         .payload(payloadFromResource("/datacenters.json")).build();
 }
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientLiveTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientLiveTest.java
index cbac274..51180b7 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientLiveTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/internal/BaseSDCClientLiveTest.java
@@ -18,16 +18,16 @@
  */
 package org.jclouds.joyent.sdc.v6_5.internal;
 
-import org.jclouds.apis.BaseContextLiveTest;
-import org.jclouds.joyent.sdc.v6_5.SDCApiMetadata;
+import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
 import org.jclouds.joyent.sdc.v6_5.SDCAsyncClient;
 import org.jclouds.joyent.sdc.v6_5.SDCClient;
 import org.jclouds.rest.RestContext;
+import org.jclouds.sshj.config.SshjSshClientModule;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.Test;
 
-import com.google.common.reflect.TypeToken;
+import com.google.inject.Module;
 
 /**
  * Tests behavior of {@code SDCClient}
@@ -35,7 +35,7 @@
  * @author Adrian Cole
  */
 @Test(groups = "live")
-public class BaseSDCClientLiveTest extends BaseContextLiveTest<RestContext<SDCClient, SDCAsyncClient>> {
+public class BaseSDCClientLiveTest extends BaseComputeServiceContextLiveTest {
 
    public BaseSDCClientLiveTest() {
       provider = "joyent-sdc";
@@ -47,7 +47,7 @@
    @Override
    public void setupContext() {
       super.setupContext();
-      sdcContext = context;
+      sdcContext = view.unwrap();
    }
 
    @AfterGroups(groups = "live")
@@ -57,8 +57,7 @@
    }
 
    @Override
-   protected TypeToken<RestContext<SDCClient, SDCAsyncClient>> contextType() {
-      return SDCApiMetadata.CONTEXT_TOKEN;
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
    }
-
 }
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseCreatedMachineTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseCreatedMachineTest.java
index 8920680..10e73ff 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseCreatedMachineTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseCreatedMachineTest.java
@@ -22,6 +22,7 @@
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.domain.JsonBall;
 import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule;
 import org.jclouds.joyent.sdc.v6_5.domain.Machine;
 import org.jclouds.joyent.sdc.v6_5.domain.Type;
@@ -37,7 +38,7 @@
 /**
  * @author Gerald Pereira
  */
-@Test(groups = "unit", testName = "ParseServerTest")
+@Test(groups = "unit", testName = "ParseMachineTest")
 public class ParseCreatedMachineTest extends BaseItemParserTest<Machine> {
 
    @Override
@@ -51,14 +52,16 @@
       return Machine
             .builder()
             .id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1")
-            .name("testJClouds")
+            .name("sample-e92")
             .type(Type.VIRTUALMACHINE)
             .state(Machine.State.STOPPED)
             .dataset("sdc:sdc:centos-5.7:1.2.1")
             .ips(ImmutableSet. <String>builder().add("37.153.96.62").add("10.224.0.63").build())
             .memorySizeMb(1024)
             .diskSizeGb(61440)
-            .metadata(ImmutableMap. <String,String>builder().put("root_authorized_keys","ssh-rsa XXXXXX== test@xxxx.ovh.net\n").build())
+            .metadata(
+                  ImmutableMap.<String, JsonBall> builder()
+                        .put("root_authorized_keys", new JsonBall("ssh-rsa XXXXXX== test@xxxx.ovh.net\n")).build())
             .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:32:46+00:00"))
             .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T08:44:53+00:00"))
             .build();
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseKeyListTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseKeyListTest.java
new file mode 100644
index 0000000..edd8d17
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseKeyListTest.java
@@ -0,0 +1,71 @@
+/**
+ * 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.joyent.sdc.v6_5.parse;
+
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+import org.jclouds.json.BaseSetParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "ParseKeyListTest")
+public class ParseKeyListTest extends BaseSetParserTest<Key> {
+
+   @Override
+   public String resource() {
+      return "/key_list.json";
+   }
+
+   @Override
+   @Consumes(MediaType.APPLICATION_JSON)
+   public Set<Key> expected() {
+      return ImmutableSet.of(
+            Key.builder()
+               .name("rsa")
+               .key("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0A5Pf5Cq...")
+               .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-04-13T22:14:46+00:00"))
+               .build()
+      );
+   }
+
+   protected Injector injector() {
+      return Guice.createInjector(new SDCParserModule(), new GsonModule() {
+
+         @Override
+         protected void configure() {
+            bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+            super.configure();
+         }
+
+      });
+   }
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseKeyTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseKeyTest.java
new file mode 100644
index 0000000..c147693
--- /dev/null
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseKeyTest.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.joyent.sdc.v6_5.parse;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule;
+import org.jclouds.joyent.sdc.v6_5.domain.Key;
+import org.jclouds.json.BaseItemParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "ParseKeyTest")
+public class ParseKeyTest extends BaseItemParserTest<Key> {
+
+   @Override
+   public String resource() {
+      return "/key.json";
+   }
+
+   @Override
+   @Consumes(MediaType.APPLICATION_JSON)
+   public Key expected() {
+      return Key.builder()
+                .name("rsa")
+                .key("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0A5Pf5Cq...")
+                .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-04-13T22:14:46+00:00"))
+                .build();
+   }
+   
+   protected Injector injector() {
+      return Guice.createInjector(new SDCParserModule(), new GsonModule() {
+
+         @Override
+         protected void configure() {
+            bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+            super.configure();
+         }
+
+      });
+   }
+}
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java
index 60d70bf..51979c7 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineListTest.java
@@ -24,6 +24,7 @@
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.domain.JsonBall;
 import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule;
 import org.jclouds.joyent.sdc.v6_5.domain.Machine;
 import org.jclouds.joyent.sdc.v6_5.domain.Type;
@@ -54,7 +55,7 @@
             Machine
                   .builder()
                   .id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1")
-                  .name("testJClouds")
+                  .name("sample-e92")
                   .type(Type.VIRTUALMACHINE)
                   .state(Machine.State.RUNNING)
                   .dataset("sdc:sdc:centos-5.7:1.2.1")
@@ -62,16 +63,16 @@
                   .memorySizeMb(1024)
                   .diskSizeGb(61440)
                   .metadata(
-                        ImmutableMap.<String, String> builder()
-                              .put("root_authorized_keys", "ssh-rsa XXXXXX== test@xxxx.ovh.net\n").build())
+                        ImmutableMap.<String, JsonBall> builder()
+                              .put("root_authorized_keys", new JsonBall("ssh-rsa XXXXXX== test@xxxx.ovh.net\n")).build())
                   .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:32:46+00:00"))
                   .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T09:00:33+00:00"))
                   .build(),
 
-            Machine.builder().id("d73cb0b0-7d1f-44ef-8c40-e040eef0f726").name("testJClouds2").type(Type.SMARTMACHINE)
+            Machine.builder().id("d73cb0b0-7d1f-44ef-8c40-e040eef0f726").name("sample-e922").type(Type.SMARTMACHINE)
                   .state(Machine.State.RUNNING).dataset("sdc:sdc:smartosplus:3.1.0")
                   .ips(ImmutableSet.<String> builder().add("37.153.96.56").add("10.224.0.57").build())
-                  .memorySizeMb(1024).diskSizeGb(61440).metadata(ImmutableMap.<String, String> of())
+                  .memorySizeMb(1024).diskSizeGb(61440).metadata(ImmutableMap.<String, JsonBall> of())
                   .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:39:43+00:00"))
                   .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:43:45+00:00"))
                   .build()
diff --git a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java
index 02829a4..912d31f 100644
--- a/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java
+++ b/labs/joyent-sdc/src/test/java/org/jclouds/joyent/sdc/v6_5/parse/ParseMachineTest.java
@@ -22,6 +22,7 @@
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.domain.JsonBall;
 import org.jclouds.joyent.sdc.v6_5.config.SDCParserModule;
 import org.jclouds.joyent.sdc.v6_5.domain.Machine;
 import org.jclouds.joyent.sdc.v6_5.domain.Type;
@@ -51,7 +52,7 @@
       return Machine
             .builder()
             .id("94eba336-ecb7-49f5-8a27-52f5e4dd57a1")
-            .name("testJClouds")
+            .name("sample-e92")
             .type(Type.VIRTUALMACHINE)
             .state(Machine.State.STOPPED)
             .dataset("sdc:sdc:centos-5.7:1.2.1")
@@ -59,8 +60,8 @@
             .memorySizeMb(1024)
             .diskSizeGb(61440)
             .metadata(
-                  ImmutableMap.<String, String> builder()
-                        .put("root_authorized_keys", "ssh-rsa XXXXXX== test@xxxx.ovh.net\n").build())
+                  ImmutableMap.<String, JsonBall> builder()
+                        .put("root_authorized_keys", new JsonBall("ssh-rsa XXXXXX== test@xxxx.ovh.net\n")).build())
             .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-09T13:32:46+00:00"))
             .updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2012-05-11T08:44:53+00:00")).build();
    }
diff --git a/labs/joyent-sdc/src/test/resources/key.json b/labs/joyent-sdc/src/test/resources/key.json
new file mode 100644
index 0000000..5957f79
--- /dev/null
+++ b/labs/joyent-sdc/src/test/resources/key.json
@@ -0,0 +1,5 @@
+{
+  "name": "rsa",
+  "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0A5Pf5Cq...",
+  "created": "2011-04-13T22:14:46+00:00"
+}
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/test/resources/key_list.json b/labs/joyent-sdc/src/test/resources/key_list.json
new file mode 100644
index 0000000..503dd9c
--- /dev/null
+++ b/labs/joyent-sdc/src/test/resources/key_list.json
@@ -0,0 +1,7 @@
+[
+  {
+    "name": "rsa",
+    "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0A5Pf5Cq...",
+    "created": "2011-04-13T22:14:46+00:00"
+  }
+]
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/test/resources/logback.xml b/labs/joyent-sdc/src/test/resources/logback.xml
deleted file mode 100644
index b77c1b0..0000000
--- a/labs/joyent-sdc/src/test/resources/logback.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0"?>

-<configuration scan="false">

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

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

-

-        <encoder>

-            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

-        </encoder>

-    </appender>

-

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

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

-

-        <encoder>

-            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

-        </encoder>

-    </appender>

-

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

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

-

-        <encoder>

-            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

-        </encoder>

-    </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>

-

-</configuration>

diff --git a/labs/joyent-sdc/src/test/resources/machine.json b/labs/joyent-sdc/src/test/resources/machine.json
index fd56f2f..4815124 100644
--- a/labs/joyent-sdc/src/test/resources/machine.json
+++ b/labs/joyent-sdc/src/test/resources/machine.json
@@ -1 +1 @@
-{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"testJClouds","type":"virtualmachine","state":"stopped","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T08:44:53+00:00"}
+{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"sample-e92","type":"virtualmachine","state":"stopped","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T08:44:53+00:00"}
diff --git a/labs/joyent-sdc/src/test/resources/machine_list.json b/labs/joyent-sdc/src/test/resources/machine_list.json
index 29ca4ca..51b82b7 100644
--- a/labs/joyent-sdc/src/test/resources/machine_list.json
+++ b/labs/joyent-sdc/src/test/resources/machine_list.json
@@ -1 +1 @@
-[{"id":"d73cb0b0-7d1f-44ef-8c40-e040eef0f726","name":"testJClouds2","type":"smartmachine","state":"running","dataset":"sdc:sdc:smartosplus:3.1.0","ips":["37.153.96.56","10.224.0.57"],"memory":1024,"disk":61440,"metadata":{},"created":"2012-05-09T13:39:43+00:00","updated":"2012-05-09T13:43:45+00:00"},{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"testJClouds","type":"virtualmachine","state":"running","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net\n"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T09:00:33+00:00"}]
\ No newline at end of file
+[{"id":"d73cb0b0-7d1f-44ef-8c40-e040eef0f726","name":"sample-e922","type":"smartmachine","state":"running","dataset":"sdc:sdc:smartosplus:3.1.0","ips":["37.153.96.56","10.224.0.57"],"memory":1024,"disk":61440,"metadata":{},"created":"2012-05-09T13:39:43+00:00","updated":"2012-05-09T13:43:45+00:00"},{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"sample-e92","type":"virtualmachine","state":"running","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net\n"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T09:00:33+00:00"}]
\ No newline at end of file
diff --git a/labs/joyent-sdc/src/test/resources/new_machine.json b/labs/joyent-sdc/src/test/resources/new_machine.json
index fd56f2f..4815124 100644
--- a/labs/joyent-sdc/src/test/resources/new_machine.json
+++ b/labs/joyent-sdc/src/test/resources/new_machine.json
@@ -1 +1 @@
-{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"testJClouds","type":"virtualmachine","state":"stopped","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T08:44:53+00:00"}
+{"id":"94eba336-ecb7-49f5-8a27-52f5e4dd57a1","name":"sample-e92","type":"virtualmachine","state":"stopped","dataset":"sdc:sdc:centos-5.7:1.2.1","ips":["37.153.96.62","10.224.0.63"],"memory":1024,"disk":61440,"metadata":{"root_authorized_keys":"ssh-rsa XXXXXX== test@xxxx.ovh.net"},"created":"2012-05-09T13:32:46+00:00","updated":"2012-05-11T08:44:53+00:00"}
diff --git a/labs/joyentcloud/pom.xml b/labs/joyentcloud/pom.xml
new file mode 100644
index 0000000..a0bdbab
--- /dev/null
+++ b/labs/joyentcloud/pom.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.jclouds</groupId>
+    <artifactId>jclouds-project</artifactId>
+    <version>1.5.0-SNAPSHOT</version>
+    <relativePath>../../project/pom.xml</relativePath>
+  </parent>
+  <groupId>org.jclouds.labs</groupId>
+  <artifactId>joyentcloud</artifactId>
+  <name>jcloud joyentcloud api</name>
+  <description>jclouds components to access the Joyent Cloud</description>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <test.joyentcloud.endpoint>https://api.joyentcloud.com</test.joyentcloud.endpoint>
+    <test.joyentcloud.api-version>~6.5</test.joyentcloud.api-version>
+    <test.joyentcloud.build-version></test.joyentcloud.build-version>
+    <test.joyentcloud.identity>FIXME_IDENTITY</test.joyentcloud.identity>
+    <test.joyentcloud.credential>FIXME_CREDENTIALS</test.joyentcloud.credential>
+    <jclouds.osgi.export>org.jclouds.joyent.joyentcloud*;version="${project.version}"</jclouds.osgi.export>
+    <jclouds.osgi.import>
+      org.jclouds.rest.internal;version="${project.version}",
+      org.jclouds*;version="${project.version}",
+      *
+    </jclouds.osgi.import>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.jclouds.labs</groupId>
+      <artifactId>joyent-sdc</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.labs</groupId>
+      <artifactId>joyent-sdc</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds</groupId>
+      <artifactId>jclouds-core</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.driver</groupId>
+      <artifactId>jclouds-slf4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.driver</groupId>
+      <artifactId>jclouds-sshj</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <version>1.0.0</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  
+  <profiles>
+    <profile>
+      <id>live</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>integration</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>test</goal>
+                </goals>
+                <configuration>
+                  <systemPropertyVariables>
+                    <test.joyentcloud.endpoint>${test.joyentcloud.endpoint}</test.joyentcloud.endpoint>
+                    <test.joyentcloud.api-version>${test.joyentcloud.api-version}</test.joyentcloud.api-version>
+                    <test.joyentcloud.build-version>${test.joyentcloud.build-version}</test.joyentcloud.build-version>
+                    <test.joyentcloud.identity>${test.joyentcloud.identity}</test.joyentcloud.identity>
+                    <test.joyentcloud.credential>${test.joyentcloud.credential}</test.joyentcloud.credential>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>
diff --git a/labs/joyentcloud/src/main/java/org/jclouds/joyent/joyentcloud/JoyentCloudProviderMetadata.java b/labs/joyentcloud/src/main/java/org/jclouds/joyent/joyentcloud/JoyentCloudProviderMetadata.java
new file mode 100644
index 0000000..9b278a7
--- /dev/null
+++ b/labs/joyentcloud/src/main/java/org/jclouds/joyent/joyentcloud/JoyentCloudProviderMetadata.java
@@ -0,0 +1,93 @@
+/**
+ * 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.joyent.joyentcloud;
+
+import static org.jclouds.location.reference.LocationConstants.ISO3166_CODES;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_ZONE;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_ZONES;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.joyent.sdc.v6_5.SDCApiMetadata;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+
+/**
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for SDC.
+ * @author Adrian Cole
+ */
+public class JoyentCloudProviderMetadata extends BaseProviderMetadata {
+
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 2196535609684739834L;
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   @Override
+   public Builder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
+
+   public JoyentCloudProviderMetadata() {
+      super(builder());
+   }
+
+   public JoyentCloudProviderMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = new Properties();
+      properties.setProperty(PROPERTY_ZONES, "us-east-1,us-west-1,us-sw-1,eu-ams-1");
+      properties.setProperty(PROPERTY_ZONE + ".us-east-1." + ISO3166_CODES, "US-VA");
+      properties.setProperty(PROPERTY_ZONE + ".us-west-1." + ISO3166_CODES, "US-CA");
+      properties.setProperty(PROPERTY_ZONE + ".us-sw-1." + ISO3166_CODES, "US-NV");
+      properties.setProperty(PROPERTY_ZONE + ".eu-ams-1." + ISO3166_CODES, "NL-NH");
+      return properties;
+   }
+
+   public static class Builder extends BaseProviderMetadata.Builder {
+
+      protected Builder() {
+         id("joyentcloud")
+         .name("JoyentCloud")
+         .apiMetadata(new SDCApiMetadata())
+         .homepage(URI.create("http://www.joyent.com/products/smartdatacenter/"))
+         .console(URI.create("https://my.joyentcloud.com/login"))
+         .iso3166Codes("US-VA", "US-CA", "US-NV", "NL-NH")
+         .endpoint("https://api.joyentcloud.com")
+         .defaultProperties(JoyentCloudProviderMetadata.defaultProperties());
+      }
+
+      @Override
+      public JoyentCloudProviderMetadata build() {
+         return new JoyentCloudProviderMetadata(this);
+      }
+
+      @Override
+      public Builder fromProviderMetadata(ProviderMetadata in) {
+         super.fromProviderMetadata(in);
+         return this;
+      }
+
+   }
+}
\ No newline at end of file
diff --git a/labs/joyentcloud/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/labs/joyentcloud/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
new file mode 100644
index 0000000..73d417c
--- /dev/null
+++ b/labs/joyentcloud/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
@@ -0,0 +1 @@
+org.jclouds.joyent.joyentcloud.JoyentCloudProviderMetadata
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudComputeServiceLiveTest.java
similarity index 63%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudComputeServiceLiveTest.java
index 771a1be..9522c5d 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudComputeServiceLiveTest.java
@@ -16,13 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.joyent.joyentcloud;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.joyent.sdc.v6_5.compute.SDCComputeServiceLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "JoyentCloudComputeServiceLiveTest")
+public class JoyentCloudComputeServiceLiveTest extends SDCComputeServiceLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public JoyentCloudComputeServiceLiveTest() {
+      provider = "joyentcloud";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudLiveTest.java
similarity index 68%
rename from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
rename to labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudLiveTest.java
index 771a1be..bc282ed 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudLiveTest.java
@@ -16,13 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.joyent.joyentcloud;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.joyent.sdc.v6_5.SDCClientLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
-
-   void startPool() throws RunNodesException;
-
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "JoyentCloudLiveTest")
+public class JoyentCloudLiveTest extends SDCClientLiveTest {
+   public JoyentCloudLiveTest() {
+      provider = "joyentcloud";
+   }
 }
diff --git a/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudProviderMetadataTest.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudProviderMetadataTest.java
new file mode 100644
index 0000000..9d99158
--- /dev/null
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudProviderMetadataTest.java
@@ -0,0 +1,35 @@
+/**
+ * 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 computee 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.joyent.joyentcloud;
+
+import org.jclouds.joyent.sdc.v6_5.SDCApiMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "JoyentCloudProviderMetadataTest")
+public class JoyentCloudProviderMetadataTest extends BaseProviderMetadataTest {
+
+   public JoyentCloudProviderMetadataTest() {
+      super(new JoyentCloudProviderMetadata(), new SDCApiMetadata());
+   }
+}
diff --git a/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudTemplateBuilderLiveTest.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudTemplateBuilderLiveTest.java
new file mode 100644
index 0000000..69ddb03
--- /dev/null
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/JoyentCloudTemplateBuilderLiveTest.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.joyent.joyentcloud;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Set;
+
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.OsFamilyVersion64Bit;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.internal.BaseTemplateBuilderLiveTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "JoyentCloudTemplateBuilderLiveTest")
+public class JoyentCloudTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest {
+
+   public JoyentCloudTemplateBuilderLiveTest() {
+      provider = "joyentcloud";
+   }
+
+   @Override
+   protected Predicate<OsFamilyVersion64Bit> defineUnsupportedOperatingSystems() {
+      return Predicates.not(new Predicate<OsFamilyVersion64Bit>() {
+
+         @Override
+         public boolean apply(OsFamilyVersion64Bit input) {
+            switch (input.family) {
+            case UBUNTU:
+               return (input.version.equals("") || input.version.equals("10.04")) && input.is64Bit;
+            case DEBIAN:
+               return input.is64Bit && !input.version.equals("5.0");
+            case CENTOS:
+               return (input.version.equals("") || input.version.equals("5.7") || input.version.equals("6.0"))
+                     && input.is64Bit;
+            default:
+               return false;
+            }
+         }
+
+      });
+   }
+
+   @Test
+   public void testTemplateBuilder() {
+      Template defaultTemplate = this.view.getComputeService().templateBuilder().build();
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "10.04");
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
+      assertEquals(defaultTemplate.getImage().getName(), "ubuntu-10.04");
+      assertEquals(defaultTemplate.getImage().getDefaultCredentials().getUser(), "root");
+      assertEquals(defaultTemplate.getLocation().getId(), "us-east-1");
+      assertEquals(defaultTemplate.getImage().getLocation().getId(), "us-east-1");
+      assertEquals(defaultTemplate.getHardware().getLocation().getId(), "us-east-1");
+   }
+
+   @Override
+   protected Set<String> getIso3166Codes() {
+      return ImmutableSet.<String> of("US-VA", "US-CA", "US-NV", "NL-NH");
+   }
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudDatacenterClientLiveTest.java
similarity index 64%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudDatacenterClientLiveTest.java
index 771a1be..c35790c 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudDatacenterClientLiveTest.java
@@ -16,13 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.joyent.joyentcloud.features;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.joyent.sdc.v6_5.features.DatacenterClientLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
-
-   void startPool() throws RunNodesException;
-
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "JoyentCloudDatacenterClientLiveTest")
+public class JoyentCloudDatacenterClientLiveTest extends DatacenterClientLiveTest {
+   public JoyentCloudDatacenterClientLiveTest() {
+      provider = "joyentcloud";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudDatasetClientLiveTest.java
similarity index 65%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudDatasetClientLiveTest.java
index 771a1be..412ec8c 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudDatasetClientLiveTest.java
@@ -16,13 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.joyent.joyentcloud.features;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.joyent.sdc.v6_5.features.DatasetClientLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
-
-   void startPool() throws RunNodesException;
-
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "JoyentCloudDatasetClientLiveTest")
+public class JoyentCloudDatasetClientLiveTest extends DatasetClientLiveTest {
+   public JoyentCloudDatasetClientLiveTest() {
+      provider = "joyentcloud";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudKeyClientLiveTest.java
similarity index 66%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudKeyClientLiveTest.java
index 771a1be..5db0864 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudKeyClientLiveTest.java
@@ -16,13 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.joyent.joyentcloud.features;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.joyent.sdc.v6_5.features.KeyClientLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
-
-   void startPool() throws RunNodesException;
-
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "JoyentCloudKeyClientLiveTest")
+public class JoyentCloudKeyClientLiveTest extends KeyClientLiveTest {
+   public JoyentCloudKeyClientLiveTest() {
+      provider = "joyentcloud";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudMachineClientLiveTest.java
similarity index 65%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudMachineClientLiveTest.java
index 771a1be..6307226 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudMachineClientLiveTest.java
@@ -16,13 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.joyent.joyentcloud.features;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.joyent.sdc.v6_5.features.MachineClientLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
-
-   void startPool() throws RunNodesException;
-
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "JoyentCloudMachineClientLiveTest")
+public class JoyentCloudMachineClientLiveTest extends MachineClientLiveTest {
+   public JoyentCloudMachineClientLiveTest() {
+      provider = "joyentcloud";
+   }
 }
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudPackageClientLiveTest.java
similarity index 65%
copy from labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
copy to labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudPackageClientLiveTest.java
index 771a1be..0e2f42c 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/PooledComputeService.java
+++ b/labs/joyentcloud/src/test/java/org/jclouds/joyent/joyentcloud/features/JoyentCloudPackageClientLiveTest.java
@@ -16,13 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.nodepool;
+package org.jclouds.joyent.joyentcloud.features;
 
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
+import org.jclouds.joyent.sdc.v6_5.features.PackageClientLiveTest;
+import org.testng.annotations.Test;
 
-public interface PooledComputeService extends ComputeService {
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "JoyentCloudPackageClientLiveTest")
+public class JoyentCloudPackageClientLiveTest extends PackageClientLiveTest {
 
-   void startPool() throws RunNodesException;
-
+   public JoyentCloudPackageClientLiveTest(){
+      provider = "joyentcloud";
+   }
 }
diff --git a/labs/nodepool/pom.xml b/labs/nodepool/pom.xml
index 1cccd6e..a60c8c7 100644
--- a/labs/nodepool/pom.xml
+++ b/labs/nodepool/pom.xml
@@ -1,12 +1,13 @@
 <?xml version="1.0"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<project
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <artifactId>jclouds-project</artifactId>
     <groupId>org.jclouds</groupId>
     <version>1.5.0-SNAPSHOT</version>
-        <relativePath>../../project/pom.xml</relativePath>
+    <relativePath>../../project/pom.xml</relativePath>
   </parent>
   <groupId>org.jclouds.labs</groupId>
   <artifactId>nodepool</artifactId>
@@ -17,23 +18,22 @@
     <jsch.version>0.1.46</jsch.version>
     <test.aws-ec2.identity>${test.aws.identity}</test.aws-ec2.identity>
     <test.aws-ec2.credential>${test.aws.credential}</test.aws-ec2.credential>
-
     <jclouds.osgi.export>org.jclouds.nodepool*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
   </properties>
   <dependencies>
-	<dependency>
-		<groupId>org.jclouds</groupId>
-		<artifactId>jclouds-compute</artifactId>
-		<version>${project.version}</version>
-	</dependency>
-   <dependency>
+    <dependency>
+      <groupId>org.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.jclouds</groupId>
       <artifactId>jclouds-core</artifactId>
       <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
-   </dependency>
+    </dependency>
     <dependency>
       <groupId>org.jclouds</groupId>
       <artifactId>jclouds-compute</artifactId>
@@ -41,41 +41,46 @@
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
-	<dependency>
-		<groupId>org.jclouds.provider</groupId>
-		<artifactId>aws-ec2</artifactId>
-		<version>${project.version}</version>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <version>1.0.0</version>
       <scope>test</scope>
-	</dependency>
-	<dependency>
-		<groupId>org.jclouds.driver</groupId>
-		<artifactId>jclouds-enterprise</artifactId>
-		<version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.provider</groupId>
+      <artifactId>aws-ec2</artifactId>
+      <version>${project.version}</version>
       <scope>test</scope>
-	</dependency>
-	<dependency>
-		<groupId>org.jclouds.driver</groupId>
-		<artifactId>jclouds-sshj</artifactId>
-		<version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.driver</groupId>
+      <artifactId>jclouds-enterprise</artifactId>
+      <version>${project.version}</version>
       <scope>test</scope>
-	</dependency>
-	<dependency>
-		<groupId>org.jclouds</groupId>
-		<artifactId>jclouds-scriptbuilder</artifactId>
-		<version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.driver</groupId>
+      <artifactId>jclouds-sshj</artifactId>
+      <version>${project.version}</version>
       <scope>test</scope>
-	</dependency>
-	<dependency>
-		<groupId>org.jclouds.driver</groupId>
-		<artifactId>jclouds-jsch</artifactId>
-		<version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds</groupId>
+      <artifactId>jclouds-scriptbuilder</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.driver</groupId>
+      <artifactId>jclouds-jsch</artifactId>
+      <version>${project.version}</version>
       <scope>test</scope>
-	</dependency>
-	<dependency>
-		<groupId>com.jcraft</groupId>
-		<artifactId>jsch</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.jcraft</groupId>
+      <artifactId>jsch</artifactId>
       <scope>test</scope>
-     </dependency>
-</dependencies>
-    
+    </dependency>
+  </dependencies>
+
 </project>
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolApiMetadata.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolApiMetadata.java
new file mode 100644
index 0000000..849349d
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolApiMetadata.java
@@ -0,0 +1,79 @@
+/*
+ * 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.nodepool;
+
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_GROUP_PROPERTY;
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MAX_SIZE_PROPERTY;
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MIN_SIZE_PROPERTY;
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.REMOVE_DESTROYED_PROPERTY;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.apis.internal.BaseApiMetadata;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.rest.internal.BaseRestApiMetadata;
+
+public class NodePoolApiMetadata extends BaseApiMetadata {
+
+   /** The serialVersionUID */
+   private static final long serialVersionUID = -2778027403592717115L;
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   @Override
+   public Builder toBuilder() {
+      return Builder.class.cast(builder().fromApiMetadata(this));
+   }
+
+   public NodePoolApiMetadata() {
+      super(builder());
+   }
+
+   protected NodePoolApiMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = BaseRestApiMetadata.defaultProperties();
+      properties.setProperty(BACKING_GROUP_PROPERTY, "nodepool");
+      properties.setProperty(MAX_SIZE_PROPERTY, 10 + "");
+      properties.setProperty(MIN_SIZE_PROPERTY, 5 + "");
+      properties.setProperty(REMOVE_DESTROYED_PROPERTY, "false");
+      return properties;
+   }
+
+   public static class Builder extends BaseApiMetadata.Builder {
+      protected Builder() {
+         id("nodepool").name("node pool provider wrapper").identityName("Unused").defaultIdentity("nodepool")
+                  .defaultEndpoint("nodepool").documentation(
+                           URI.create("http://www.jclouds.org/documentation/userguide/compute")).view(
+                           ComputeServiceContext.class).defaultProperties(NodePoolApiMetadata.defaultProperties());
+      }
+
+      @Override
+      public NodePoolApiMetadata build() {
+         return new NodePoolApiMetadata(this);
+      }
+
+   }
+
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeService.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeService.java
new file mode 100644
index 0000000..c656809
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeService.java
@@ -0,0 +1,80 @@
+/*
+ * 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.nodepool;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.nodepool.config.NodePoolComputeServiceProperties;
+import org.jclouds.nodepool.internal.EagerNodePoolComputeService;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.inject.ImplementedBy;
+
+/**
+ * A {@link ComputeService} wrapper that uses a pool of pre-loaded nodes to speed up creation times.
+ * 
+ * This interface extends the ComputeService with a backing pool of nodes, configured during
+ * construction. The {@link #startPool()} and {@link #close()} methods are used to create and
+ * destroy the pool and its associated nodes.
+ * 
+ * @author Andrew Kennedy
+ * @author Gustavo Morozowski
+ * @author David Alves
+ * 
+ * @see <a href="https://github.com/jclouds/jclouds/wiki/NodePool-Notes">NodePool Notes</a>
+ * @see NodePoolComputeServiceProperties
+ * @since 1.5.0
+ */
+@ImplementedBy(EagerNodePoolComputeService.class)
+public interface NodePoolComputeService extends ComputeService, Closeable {
+
+   /**
+    * Starts the pool, may or may not start the actual nodes, depending on the implementation, i.e.
+    * the returned Set may be empty.
+    */
+   ListenableFuture<Void> startPool();
+
+   /**
+    * Returns true of the pool has been started by calling the {@link #startPool()} method.
+    */
+   boolean isStarted();
+
+   /**
+    * Returns the number of ready (pre-allocated) nodes in the pool.
+    */
+   int ready();
+
+   /**
+    * Returns the current size of the pool (nodes allocated on the backing compute service)
+    */
+   int size();
+
+   /**
+    * Returns the maximum amout of node the pool will allocate in the backing compute service.
+    */
+   int maxSize();
+
+   /**
+    * Close the pool and destroy all associated nodes.
+    */
+   void close() throws IOException;
+
+}
\ No newline at end of file
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceProperties.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceProperties.java
new file mode 100644
index 0000000..96fae93
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceProperties.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.nodepool.config;
+
+/**
+ * Constants for the {@link org.jclouds.nodepool.NodePoolComputeService}.
+ */
+public interface NodePoolComputeServiceProperties {
+
+   /**
+    * Property to set the name of the backing group used for pooled nodes.
+    */
+   public static final String BACKING_GROUP_PROPERTY = "jclouds.nodepool.backing-group";
+
+   /**
+    * Property to set the template that will be used to create the nodes in the pool.
+    */
+   public static final String BACKING_TEMPLATE_PROPERTY = "jclouds.nodepool.backing-template";
+
+   /**
+    * Property to set the maximum size of the pool. Set this to {@literal -1} to have an unlimited
+    * pool size.
+    */
+   public static final String MAX_SIZE_PROPERTY = "jclouds.nodepool.max-size";
+
+   /**
+    * Property to set the minimum (initial) size of the pool.
+    */
+   public static final String MIN_SIZE_PROPERTY = "jclouds.nodepool.min-size";
+
+   /**
+    * Property to set the pool behaviour to remove destroyed nodes rather than returning them to the
+    * pool for re-use.
+    */
+   public static final String REMOVE_DESTROYED_PROPERTY = "jclouds.nodepool.remove-destroyed";
+
+}
\ No newline at end of file
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeService.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeService.java
new file mode 100644
index 0000000..554cd46
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeService.java
@@ -0,0 +1,335 @@
+package org.jclouds.nodepool.internal;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.find;
+
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.RunScriptOnNodesException;
+import org.jclouds.compute.domain.ComputeMetadata;
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.options.RunScriptOptions;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.domain.Location;
+import org.jclouds.nodepool.NodePoolComputeService;
+import org.jclouds.scriptbuilder.domain.Statement;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * A base class for {@link NodePoolComputeService}, takes care of keeping (not changing assignments)
+ * and of everything that does not change the pool.
+ * 
+ * @author David Alves
+ * 
+ */
+public abstract class BaseNodePoolComputeService implements NodePoolComputeService {
+
+   protected final ComputeService backingComputeService;
+   protected final String poolGroupName;
+   protected final Template template;
+   protected final Image image;
+   protected final Hardware hardware;
+   protected final Location location;
+
+   // assignments of nodes to group names
+   protected final Multimap<String, NodeMetadata> assignments = HashMultimap.create();
+
+   protected final AtomicBoolean started = new AtomicBoolean(false);
+
+   public BaseNodePoolComputeService(ComputeServiceContext backingComputeServiceContext, String poolGroupNamePrefix,
+            Template backingTemplate) {
+      this.backingComputeService = backingComputeServiceContext.getComputeService();
+      this.poolGroupName = poolGroupNamePrefix;
+      this.template = backingTemplate == null ? this.backingComputeService.templateBuilder().build() : backingTemplate;
+      this.image = this.template.getImage();
+      this.hardware = this.template.getHardware();
+      this.location = this.template.getLocation();
+   }
+
+   /**
+    * Checks which nodes match the users predicate and builds a predicate that returns true to their
+    * specific ids.
+    * 
+    * @param filter
+    * @return
+    */
+   private Predicate<NodeMetadata> transformUserPredicateSpecificIdPredicate(Predicate<NodeMetadata> filter) {
+      Iterable<Map.Entry<String, NodeMetadata>> relevantAssginemnts = filterAssignmentsBasedOnUserPredicate(filter);
+      final Set<String> ids = Sets.newHashSet();
+      for (Map.Entry<String, NodeMetadata> assignment : relevantAssginemnts) {
+         ids.add(assignment.getValue().getId());
+      }
+      return new Predicate<NodeMetadata>() {
+         @Override
+         public boolean apply(NodeMetadata input) {
+            return ids.contains(input.getId());
+         }
+      };
+
+   }
+
+   @Override
+   public boolean isStarted() {
+      return started.get();
+   }
+
+   // TODO this is n^2 expensive. s
+   private Map<? extends NodeMetadata, ExecResponse> transformBackendExecutionMapIntoFrontend(
+            Map<? extends NodeMetadata, ExecResponse> backendMap) {
+      Map<NodeMetadata, ExecResponse> frontendMap = Maps.newHashMapWithExpectedSize(backendMap.size());
+      for (Map.Entry<? extends NodeMetadata, ExecResponse> entry : backendMap.entrySet()) {
+         Map.Entry<String, NodeMetadata> assignmentEntry = findAssigmentEntry(entry.getKey().getId());
+         frontendMap
+                  .put(toFrontendNodemetadata(assignmentEntry.getValue(), assignmentEntry.getKey()), entry.getValue());
+      }
+      return frontendMap;
+   }
+
+   protected Map.Entry<String, NodeMetadata> findAssigmentEntry(final String id) {
+      // TODO reverse lookup data structure would be faster but will pools be that big ?
+      return find(assignments.entries(), new Predicate<Map.Entry<String, NodeMetadata>>() {
+         @Override
+         public boolean apply(Entry<String, NodeMetadata> entry) {
+            return entry.getValue().getId().equals(id);
+         }
+      });
+   }
+
+   protected NodeMetadata toFrontendNodemetadata(NodeMetadata backendNodeMetadata, String group) {
+      return NodeMetadataBuilder.fromNodeMetadata(backendNodeMetadata).group(group).build();
+   }
+
+   /**
+    * Because a lot of predicates are based on group info we need that to check wether the predicate
+    * matches.
+    */
+   protected Iterable<Map.Entry<String, NodeMetadata>> filterAssignmentsBasedOnUserPredicate(
+            final Predicate<NodeMetadata> userFilter) {
+      return filter(assignments.entries(), new Predicate<Map.Entry<String, NodeMetadata>>() {
+         @Override
+         public boolean apply(Entry<String, NodeMetadata> input) {
+            return userFilter.apply(toFrontendNodemetadata(input.getValue(), input.getKey()));
+         }
+      });
+   }
+
+   @Override
+   public NodeMetadata getNodeMetadata(String id) {
+      checkState(started.get(), "pool is not started");
+      Map.Entry<String, NodeMetadata> assigmentEntry = findAssigmentEntry(id);
+      return toFrontendNodemetadata(assigmentEntry.getValue(), assigmentEntry.getKey());
+   }
+
+   @Override
+   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
+            String runScript) throws RunScriptOnNodesException {
+      return runScriptOnNodesMatching(filter, runScript, new RunScriptOptions());
+   }
+
+   @Override
+   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
+            Statement runScript) throws RunScriptOnNodesException {
+      return runScriptOnNodesMatching(filter, runScript, new RunScriptOptions());
+   }
+
+   @Override
+   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
+            String runScript, RunScriptOptions options) throws RunScriptOnNodesException {
+      checkState(started.get(), "pool is not started");
+      return transformBackendExecutionMapIntoFrontend(backingComputeService.runScriptOnNodesMatching(
+               transformUserPredicateSpecificIdPredicate(filter), runScript, options));
+   }
+
+   @Override
+   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
+            Statement runScript, RunScriptOptions options) throws RunScriptOnNodesException {
+      checkState(started.get(), "pool is not started");
+      return transformBackendExecutionMapIntoFrontend(backingComputeService.runScriptOnNodesMatching(
+               transformUserPredicateSpecificIdPredicate(filter), runScript, options));
+   }
+
+   @Override
+   public Set<? extends ComputeMetadata> listNodes() {
+      checkState(started.get(), "pool is not started");
+      return listNodesDetailsMatching(Predicates.alwaysTrue());
+   }
+
+   @SuppressWarnings( { "rawtypes", "unchecked" })
+   @Override
+   public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate filter) {
+      checkState(started.get(), "pool is not started");
+      return FluentIterable.from(filterAssignmentsBasedOnUserPredicate(filter)).transform(
+               new Function<Map.Entry<String, NodeMetadata>, NodeMetadata>() {
+                  @Override
+                  public NodeMetadata apply(Entry<String, NodeMetadata> input) {
+                     return toFrontendNodemetadata(input.getValue(), input.getKey());
+                  }
+               }).toImmutableSet();
+   }
+
+   @Override
+   public void rebootNodesMatching(final Predicate<NodeMetadata> filter) {
+      checkState(started.get(), "pool is not started");
+      backingComputeService.rebootNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
+   }
+
+   @Override
+   public void resumeNodesMatching(Predicate<NodeMetadata> filter) {
+      checkState(started.get(), "pool is not started");
+      backingComputeService.resumeNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
+   }
+
+   @Override
+   public void suspendNodesMatching(Predicate<NodeMetadata> filter) {
+      checkState(started.get(), "pool is not started");
+      backingComputeService.suspendNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
+   }
+
+   @Override
+   public ComputeServiceContext getContext() {
+      // not sure this is enough, should we have our own?
+      return backingComputeService.getContext();
+   }
+
+   // we ignore user provided templates and options
+
+   @Override
+   public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, Template template)
+            throws RunNodesException {
+      return createNodesInGroup(group, count);
+   }
+
+   @Override
+   public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, TemplateOptions templateOptions)
+            throws RunNodesException {
+      return createNodesInGroup(group, count);
+   }
+
+   @Override
+   public TemplateBuilder templateBuilder() {
+      return backingComputeService.templateBuilder().fromTemplate(template);
+   }
+
+   @Override
+   public TemplateOptions templateOptions() {
+      return backingComputeService.templateOptions();
+   }
+
+   @Override
+   public Set<? extends Hardware> listHardwareProfiles() {
+      return ImmutableSet.of(hardware);
+   }
+
+   @Override
+   public Set<? extends Image> listImages() {
+      return ImmutableSet.of(image);
+
+   }
+
+   @Override
+   public Image getImage(String id) {
+      return image.getId().equals(id) ? image : null;
+   }
+
+   @Override
+   public Set<? extends Location> listAssignableLocations() {
+      return ImmutableSet.of(location);
+   }
+
+   @Override
+   public void suspendNode(String id) {
+      if (findAssigmentEntry(id) != null) {
+         backingComputeService.suspendNode(id);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public void resumeNode(String id) {
+      if (findAssigmentEntry(id) != null) {
+         backingComputeService.resumeNode(id);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public void rebootNode(String id) {
+      if (findAssigmentEntry(id) != null) {
+         backingComputeService.rebootNode(id);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public ExecResponse runScriptOnNode(String id, Statement runScript) {
+      if (findAssigmentEntry(id) != null) {
+         return runScriptOnNode(id, runScript, new RunScriptOptions());
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public ExecResponse runScriptOnNode(String id, String runScript) {
+      if (findAssigmentEntry(id) != null) {
+         return runScriptOnNode(id, runScript, new RunScriptOptions());
+      }
+      throw new NoSuchElementException(id);
+
+   }
+
+   @Override
+   public ExecResponse runScriptOnNode(String id, Statement runScript, RunScriptOptions options) {
+      if (findAssigmentEntry(id) != null) {
+         return backingComputeService.runScriptOnNode(id, runScript, options);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public ListenableFuture<ExecResponse> submitScriptOnNode(String id, Statement runScript, RunScriptOptions options) {
+      if (findAssigmentEntry(id) != null) {
+         return backingComputeService.submitScriptOnNode(id, runScript, options);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public ExecResponse runScriptOnNode(String id, String runScript, RunScriptOptions options) {
+      if (findAssigmentEntry(id) != null) {
+         return backingComputeService.runScriptOnNode(id, runScript, options);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public Optional<ImageExtension> getImageExtension() {
+      return Optional.absent();
+   }
+
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BasePooledComputeService.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BasePooledComputeService.java
deleted file mode 100644
index 725be05..0000000
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BasePooledComputeService.java
+++ /dev/null
@@ -1,270 +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.nodepool.internal;
-
-import java.util.Map;
-import java.util.Set;
-
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.ImageExtension;
-import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.RunScriptOnNodesException;
-import org.jclouds.compute.domain.ComputeMetadata;
-import org.jclouds.compute.domain.ExecResponse;
-import org.jclouds.compute.domain.Hardware;
-import org.jclouds.compute.domain.Image;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.NodeMetadataBuilder;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.domain.TemplateBuilder;
-import org.jclouds.compute.options.RunScriptOptions;
-import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.domain.Location;
-import org.jclouds.nodepool.PooledComputeService;
-import org.jclouds.scriptbuilder.domain.Statement;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ListenableFuture;
-
-public class BasePooledComputeService implements PooledComputeService {
-
-   private final ComputeService backingComputeService;
-   private final String backingGroup;
-   private final Template backingTemplate;
-   private final int minPoolSize;
-   private Map<NodeMetadata, String> groupMapping;
-
-   public BasePooledComputeService(ComputeService backingComputeService, String backingGroup, Template backingTemplate, int minPoolSize) {
-      this.backingComputeService = backingComputeService;
-      this.backingGroup = backingGroup;
-      this.backingTemplate = backingTemplate;
-      this.minPoolSize = minPoolSize;
-   }
-
-   @Override
-   public void startPool() throws RunNodesException {
-      Set<? extends NodeMetadata> backingNodes =
-            backingComputeService.createNodesInGroup(backingGroup, minPoolSize, backingTemplate);
-      groupMapping = Maps.newHashMap();
-      for (NodeMetadata node : backingNodes) {
-         groupMapping.put(node, "unassigned");
-      }
-   }
-
-   @Override
-   public ComputeServiceContext getContext() {
-      return backingComputeService.getContext();
-   }
-
-   @Override
-   public TemplateBuilder templateBuilder() {
-      return backingComputeService.templateBuilder();
-   }
-
-   @Override
-   public TemplateOptions templateOptions() {
-      return backingComputeService.templateOptions();
-   }
-
-   @Override
-   public Set<? extends Hardware> listHardwareProfiles() {
-      return ImmutableSet.<Hardware>of(backingTemplate.getHardware());
-   }
-
-   @Override
-   public Set<? extends Image> listImages() {
-      return ImmutableSet.<Image>of(backingTemplate.getImage());
-   }
-
-   @Override
-   public Set<? extends ComputeMetadata> listNodes() {
-      Set<NodeMetadata> allocatedNodes = Sets.newLinkedHashSet();
-      for (ComputeMetadata node : backingComputeService.listNodes()) {
-         NodeMetadata metadata = backingComputeService.getNodeMetadata(node.getId());
-         String group = groupMapping.get(node);
-         if ("unassigned".equals(group))
-            continue;
-         NodeMetadata nodeWithUpdatedGroup =
-               NodeMetadataBuilder.fromNodeMetadata(metadata).group(group).build();
-         allocatedNodes.add(nodeWithUpdatedGroup);
-      }
-      return allocatedNodes;
-   }
-
-   @Override
-   public Set<? extends Location> listAssignableLocations() {
-      return ImmutableSet.<Location>of(backingTemplate.getLocation());
-   }
-
-   @Override
-   public Set<? extends NodeMetadata> createNodesInGroup(String group,
-         int count, Template template) throws RunNodesException {
-      throw new RuntimeException("not implemented");
-   }
-
-   @Override
-   public Set<? extends NodeMetadata> createNodesInGroup(String group,
-         int count, TemplateOptions templateOptions)
-               throws RunNodesException {
-      throw new RuntimeException("not implemented");
-   }
-
-   @Override
-   public Set<? extends NodeMetadata> createNodesInGroup(String group,
-         int count) throws RunNodesException {
-      int allocatedCount = 0;
-      Set<NodeMetadata> allocatedNodes = Sets.newLinkedHashSet();
-      for (NodeMetadata metadata : groupMapping.keySet()) {
-         if (groupMapping.get(metadata).equals("unassigned")) {
-            groupMapping.put(metadata, "group");
-            NodeMetadata nodeWithUpdatedGroup =
-                  NodeMetadataBuilder.fromNodeMetadata(metadata).group(group).build();
-            allocatedNodes.add(nodeWithUpdatedGroup);
-            allocatedCount += 1;
-            if (allocatedCount == count) break;
-         }
-      }
-      return allocatedNodes;
-   }
-
-   @Override
-   public void resumeNode(String id) {
-      backingComputeService.resumeNode(id);
-
-   }
-
-   @Override
-   public void resumeNodesMatching(Predicate<NodeMetadata> filter) {
-      backingComputeService.resumeNodesMatching(filter);
-
-   }
-
-   @Override
-   public void suspendNode(String id) {
-      backingComputeService.suspendNode(id);
-   }
-
-   @Override
-   public void suspendNodesMatching(Predicate<NodeMetadata> filter) {
-      backingComputeService.suspendNodesMatching(filter);
-   }
-
-   @Override
-   public void destroyNode(String id) {
-
-      backingComputeService.destroyNode(id);
-   }
-
-   @Override
-   public Set<? extends NodeMetadata> destroyNodesMatching(
-         Predicate<NodeMetadata> filter) {
-      return backingComputeService.destroyNodesMatching(filter);
-   }
-
-   @Override
-   public void rebootNode(String id) {
-      backingComputeService.rebootNode(id);
-
-   }
-
-   @Override
-   public void rebootNodesMatching(Predicate<NodeMetadata> filter) {
-      backingComputeService.rebootNodesMatching(filter);
-
-   }
-
-   @Override
-   public NodeMetadata getNodeMetadata(String id) {
-      return backingComputeService.getNodeMetadata(id);
-   }
-
-   @Override
-   public Set<? extends NodeMetadata> listNodesDetailsMatching(
-         Predicate<ComputeMetadata> filter) {
-      return backingComputeService.listNodesDetailsMatching(filter);
-   }
-
-   @Override
-   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(
-         Predicate<NodeMetadata> filter, String runScript)
-               throws RunScriptOnNodesException {
-      // TODO Auto-generated method stub
-      return backingComputeService.runScriptOnNodesMatching(filter, runScript);
-   }
-
-   @Override
-   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(
-         Predicate<NodeMetadata> filter, Statement runScript)
-               throws RunScriptOnNodesException {
-      return backingComputeService.runScriptOnNodesMatching(filter, runScript);
-   }
-
-   @Override
-   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(
-         Predicate<NodeMetadata> filter, String runScript,
-         RunScriptOptions options) throws RunScriptOnNodesException {
-      return backingComputeService.runScriptOnNodesMatching(filter, runScript, options);
-   }
-
-   @Override
-   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(
-         Predicate<NodeMetadata> filter, Statement runScript,
-         RunScriptOptions options) throws RunScriptOnNodesException {
-      return backingComputeService.runScriptOnNodesMatching(filter, runScript, options);
-   }
-
-   @Override
-   public ExecResponse runScriptOnNode(String id, Statement runScript,
-         RunScriptOptions options) {
-      return backingComputeService.runScriptOnNode(id, runScript, options);
-   }
-
-   @Override
-   public ListenableFuture<ExecResponse> submitScriptOnNode(String id,
-         Statement runScript, RunScriptOptions options) {
-      return backingComputeService.submitScriptOnNode(id, runScript, options);
-   }
-
-   @Override
-   public ExecResponse runScriptOnNode(String id, Statement runScript) {
-      return backingComputeService.runScriptOnNode(id, runScript);
-   }
-
-   @Override
-   public ExecResponse runScriptOnNode(String id, String runScript,
-         RunScriptOptions options) {
-      return backingComputeService.runScriptOnNode(id, runScript, options);
-   }
-
-   @Override
-   public ExecResponse runScriptOnNode(String id, String runScript) {
-      return backingComputeService.runScriptOnNode(id, runScript);
-   }
-
-   @Override
-   public Optional<ImageExtension> getImageExtension() {
-      return Optional.absent();
-   }
-
-}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeService.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeService.java
new file mode 100644
index 0000000..f3d0481d
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeService.java
@@ -0,0 +1,250 @@
+package org.jclouds.nodepool.internal;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.removeIf;
+import static com.google.common.collect.Iterables.transform;
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_GROUP_PROPERTY;
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_TEMPLATE_PROPERTY;
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MAX_SIZE_PROPERTY;
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MIN_SIZE_PROPERTY;
+import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.REMOVE_DESTROYED_PROPERTY;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.annotation.Nullable;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.Constants;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.NodeMetadata.Status;
+import org.jclouds.compute.predicates.NodePredicates;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.concurrent.Futures;
+import org.jclouds.logging.Logger;
+import org.jclouds.nodepool.NodePoolComputeService;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * An eager {@link NodePoolComputeService}. Eagerly builds and maintains a pool of nodes. It's only
+ * "started" after min nodes are allocated and available.
+ * 
+ * @author David Alves
+ * 
+ */
+public class EagerNodePoolComputeService extends BaseNodePoolComputeService {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final int maxSize;
+   private final boolean reuseDestroyed;
+   private final int minSize;
+   private final ExecutorService executor;
+
+   // set of available nodes
+   private Set<NodeMetadata> available = Sets.newHashSet();
+
+   // lock associated with changes to the pool since they happen asynchronously
+   private final Lock lock = new ReentrantLock();
+
+   // all the nodes in the pool (associated or not)
+   private final Set<NodeMetadata> poolNodes = Sets.newLinkedHashSet();
+
+   @Inject
+   public EagerNodePoolComputeService(ComputeServiceContext backingComputeServiceContext,
+            @Named(BACKING_GROUP_PROPERTY) String poolGroupPrefix,
+            @Named(MAX_SIZE_PROPERTY) int maxSize, @Named(MIN_SIZE_PROPERTY) int minSize,
+            @Named(REMOVE_DESTROYED_PROPERTY) boolean readdDestroyed,
+            @Nullable @Named(BACKING_TEMPLATE_PROPERTY) Template backingTemplate,
+            @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
+      super(backingComputeServiceContext, poolGroupPrefix, backingTemplate);
+      this.maxSize = maxSize;
+      this.minSize = minSize;
+      this.reuseDestroyed = readdDestroyed;
+      this.executor = executor;
+   }
+
+   @Override
+   public synchronized Set<? extends NodeMetadata> createNodesInGroup(String group, int count) throws RunNodesException {
+      checkState(started.get(), "pool is not started");
+      try {
+         return assignPoolNodes(group, count);
+      } catch (Exception e) {
+         Set<NodeMetadata> nodes = Collections.emptySet();
+         Map<String, Exception> executionExceptions = ImmutableMap.of("poolnode", e);
+         Map<NodeMetadata, Exception> failedNodes = ImmutableMap.of(new NodeMetadataBuilder().id("poolnode").status(
+                  Status.ERROR).build(), e);
+         throw new RunNodesException(group, count, template, nodes, executionExceptions, failedNodes);
+      }
+   }
+
+   @Override
+   public synchronized void destroyNode(String id) {
+      checkState(started.get(), "pool is not started");
+      unassignNode(id);
+   }
+
+   @Override
+   public synchronized Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
+      checkState(started.get(), "pool is not started");
+      // copy the set of nodes to unassign because we'll be altering the assignments map.
+      Set<Map.Entry<String, NodeMetadata>> poolNodesToUnassign = Sets
+               .newHashSet(filterAssignmentsBasedOnUserPredicate(filter));
+      // TODO this should be done in parallel since it can take quite a while, moreover the contract
+      // for any destroy node action should probably be that the pool has at least minSize nodes
+      // before it returns. need to think it through a bit better.
+      for (Map.Entry<String, NodeMetadata> poolNode : poolNodesToUnassign) {
+         unassignNode(poolNode.getValue().getId());
+      }
+      return Sets.newHashSet(transform(poolNodesToUnassign,
+               new Function<Map.Entry<String, NodeMetadata>, NodeMetadata>() {
+                  @Override
+                  public NodeMetadata apply(final Map.Entry<String, NodeMetadata> input) {
+                     assignments.remove(input.getKey(), input.getValue());
+                     return toFrontendNodemetadata(input.getValue(), input.getKey());
+                  }
+               }));
+
+   }
+
+   /**
+    * Adds nodes to the pool, using the pool's group name. Lock the pool so that no-one tries to
+    * increase/decrease until we're finished but we'll return from the method well before the pool
+    * as enough nodes.
+    */
+   private ListenableFuture<Void> increasePoolSize(final int size) {
+      lock.lock();
+      logger.debug(">> increasing pool size,  available: %s total: %s min; %s max: %s increasing to: %s", available
+               .size(), poolNodes.size(), minSize, maxSize, size);
+      return Futures.makeListenable(executor.submit(new Callable<Void>() {
+         @Override
+         public Void call() throws Exception {
+            try {
+               Set<? extends NodeMetadata> original = backingComputeService.createNodesInGroup(poolGroupName, size,
+                        template);
+               poolNodes.addAll(original);
+               available.addAll(original);
+               logger.debug("<< pool size increased, available: %s total: %s min; %s max: %s increasing to: %s",
+                        available.size(), poolNodes.size(), minSize, maxSize, size);
+               if (started.compareAndSet(false, true)) {
+                  logger.info("pool started, status: %s min; %s max: %s", available.size(), minSize, maxSize);
+               }
+               return null;
+            } finally {
+               lock.unlock();
+            }
+         }
+      }), executor);
+   }
+
+   /**
+    * Unassigns the node with the provided id. If the we're set to reuse the nodes it adds it to the
+    * available pool, if not is destroys the backing node, removes if from the poll and increases
+    * the pool size by one.
+    */
+   private NodeMetadata unassignNode(final String nodeId) {
+      Map.Entry<String, NodeMetadata> entry = findAssigmentEntry(nodeId);
+      assignments.remove(entry.getKey(), entry.getValue());
+      // if we're reusing destroyed simply add to the available nodes
+      if (reuseDestroyed) {
+         available.add(entry.getValue());
+         return entry.getValue();
+      }
+      // if not we need to destroy the backing node
+      lock.lock();
+      try {
+         backingComputeService.destroyNode(nodeId);
+         removeIf(poolNodes, new Predicate<NodeMetadata>() {
+            @Override
+            public boolean apply(NodeMetadata input) {
+               return input.getId().equals(nodeId);
+            }
+         });
+         if (poolNodes.size() < minSize) {
+            increasePoolSize(1);
+         }
+      } finally {
+         lock.unlock();
+      }
+      return entry.getValue();
+   }
+
+   /**
+    * Used to assign size pool nodes to a group. If not enough nodes are available we check if we
+    * can increase the pool if that is enough, otherwise we complain.
+    */
+   private Set<? extends NodeMetadata> assignPoolNodes(String groupName, int size) throws InterruptedException,
+            ExecutionException {
+      if (available.size() < size) {
+         if (poolNodes.size() + size > maxSize) {
+            // TODO think of a better exception
+            throw new IllegalStateException(
+                     "not enough nodes available  and cannot add enough nodes to pool [available: " + available.size()
+                              + " total: " + poolNodes.size() + " min: " + minSize + " max: " + maxSize
+                              + " requested: " + size + "]");
+         }
+         increasePoolSize(size - available.size()).get();
+      }
+      Set<NodeMetadata> groupNodes = Sets.newHashSet();
+      Iterator<NodeMetadata> iter = available.iterator();
+      for (int i = 0; i < size && iter.hasNext(); i++) {
+         NodeMetadata node = iter.next();
+         assignments.put(groupName, node);
+         iter.remove();
+         groupNodes.add(toFrontendNodemetadata(node, groupName));
+      }
+      return groupNodes;
+   }
+
+   @Override
+   public ListenableFuture<Void> startPool() {
+      return increasePoolSize(minSize);
+   }
+
+   @Override
+   public void close() {
+      // lock just to make sure we have the correct pool size
+      if (started.compareAndSet(true, false)) {
+         logger.info("Closing pooled compute service with {} nodes", size());
+         available.clear();
+         assignments.clear();
+         backingComputeService.destroyNodesMatching(NodePredicates.inGroup(poolGroupName));
+      }
+   }
+
+   @Override
+   public int ready() {
+      return available.size();
+   }
+
+   @Override
+   public int size() {
+      return poolNodes.size();
+   }
+
+   @Override
+   public int maxSize() {
+      return maxSize;
+   }
+
+}
diff --git a/labs/nodepool/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/labs/nodepool/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..64dfe90
--- /dev/null
+++ b/labs/nodepool/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.nodepool.NodePoolApiMetadata
\ No newline at end of file
diff --git a/labs/nodepool/src/test/java/org/jclouds/nodepool/AppTest.java b/labs/nodepool/src/test/java/org/jclouds/nodepool/AppTest.java
deleted file mode 100644
index d198d7d..0000000
--- a/labs/nodepool/src/test/java/org/jclouds/nodepool/AppTest.java
+++ /dev/null
@@ -1,287 +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.nodepool;
-
-import static com.google.common.base.Throwables.propagate;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static org.jclouds.scriptbuilder.domain.Statements.newStatementList;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Properties;
-import java.util.logging.Logger;
-
-import junit.framework.TestCase;
-
-import org.jclouds.Constants;
-import org.jclouds.ContextBuilder;
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.domain.ComputeMetadata;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.NodeState;
-import org.jclouds.compute.domain.OsFamily;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.domain.TemplateBuilder;
-import org.jclouds.enterprise.config.EnterpriseConfigurationModule;
-import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
-import org.jclouds.nodepool.internal.BasePooledComputeService;
-import org.jclouds.scriptbuilder.domain.Statement;
-import org.jclouds.scriptbuilder.domain.Statements;
-import org.jclouds.scriptbuilder.statements.java.InstallJDK;
-import org.jclouds.scriptbuilder.statements.login.AdminAccess;
-import org.jclouds.sshj.config.SshjSshClientModule;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.inject.Module;
-
-/**
- * Unit test for simple App.
- */
-public class AppTest extends TestCase {
-   private String identity;
-   private String credential;
-   private String providerName;
-   private File privateKey;
-   private File publicKey;
-   private String endPointUrl;
-   public String profile;
-   private int retentionTime;
-   public int instanceCap;
-
-   private String imageId;
-   private String osFamily;
-   private String osVersion;
-   private String hardwareId;
-   private int ram;
-   private int cores;
-   private String initScript;
-   private String name;
-   private boolean stopOnTerminate;
-   private ComputeService compute;
-   private PooledComputeService pooledCompute;
-   private Collection<NodeMetadata> nodes = new LinkedList<NodeMetadata>();
-   private static Logger LOGGER = Logger.getLogger("AppTest");
-   
-   private long time;
-
-   @Override
-   protected void setUp() throws Exception {
-      // setup JCloudsCloud
-      identity = "insert-your-identity-here";
-      credential = "insert-your-credential-here";
-      providerName = "aws-ec2";
-      privateKey = new File("private-key");
-      publicKey = new File("public-key");
-      endPointUrl = "";
-      profile = "aws-slave-profile";
-      retentionTime = -1;
-      instanceCap = 3;
-
-      // cloud instance template
-      name = "aws-jenkins-slave";
-      // numExecutors = 1;
-      // description = ""
-      imageId = "us-east-1/ami-4dad7424";
-      osFamily = "";
-      osVersion = "";
-      hardwareId = "t1.micro";
-      ram = -1;
-      cores = -1;
-      // labels = "whii";
-      initScript = "touch /tmp/hellothere";
-      stopOnTerminate = true;
-   }
-
-   /**
-    * Rigourous Test :-)
-    */
-   public void testApp() {
-      createCompute();
-      assertNotNull(compute);
-      createAndStartPool();
-      assertNotNull(pooledCompute);
-      for (int i = 0; i < 3; i++) {
-         startCounter();
-         provision("pool-1");
-         stopCounter();
-      }
-      for (int i = 0; i < 3; i++) {
-         startCounter();
-         provision("pool-2");
-         stopCounter();
-      }
-      for (int i = 0; i < 3; i++) {
-         startCounter();
-         provision("pool-3");
-         stopCounter();
-      }
-      assertEquals(9, getRunningNodesCount());
-      for (NodeMetadata slave : nodes) {
-         assertNotNull(slave);
-         LOGGER.info(slave.getId() + "-" + slave.getGroup());
-         terminate(slave);
-      }
-      assertEquals(0, getRunningNodesCount());
-   }
-
-   private void stopCounter() {
-      LOGGER.info("Elapsed time: " + (System.currentTimeMillis() - time));
-   }
-
-   private void startCounter() {
-      time = System.currentTimeMillis();      
-   }
-
-   public AppTest getCloud() {
-      return this;
-   }
-
-   public ComputeService getCompute() {
-      return pooledCompute;
-   }
-   
-   public NodeMetadata provision(String groupName) {
-      LOGGER.info("Provisioning new node");
-      NodeMetadata nodeMetadata = createNodeWithJdk(groupName);
-      nodes.add(nodeMetadata);
-      return nodeMetadata;
-   }
-
-   public int getRunningNodesCount() {
-      int nodeCount = 0;
-
-      for (ComputeMetadata cm : pooledCompute.listNodes()) {
-         if (NodeMetadata.class.isInstance(cm)) {
-            String nodeGroup = ((NodeMetadata) cm).getGroup();
-
-            if (!((NodeMetadata) cm).getState().equals(Status.SUSPENDED)
-                  && !((NodeMetadata) cm).getState().equals(Status.TERMINATED)) {
-               nodeCount++;
-            }
-         }
-      }
-      return nodeCount;
-   }
-
-   private void createCompute() {
-      Properties overrides = new Properties();
-      if (!Strings.isNullOrEmpty(this.endPointUrl)) {
-         overrides.setProperty(Constants.PROPERTY_ENDPOINT, this.endPointUrl);
-      }
-      Iterable<Module> modules = ImmutableSet.<Module> of(new SshjSshClientModule(), new SLF4JLoggingModule(),
-            new EnterpriseConfigurationModule());
-      this.compute = ContextBuilder.newBuilder(providerName)
-                                   .credentials(identity, credential)
-                                   .modules(modules)
-                                   .overrides(overrides).buildView(ComputeServiceContext.class).getComputeService();
-   }
-
-   private void createAndStartPool() {
-      LOGGER.info("creating jclouds nodepool");
-      ImmutableMap<String, String> userMetadata = ImmutableMap.of("Name", name);
-      TemplateBuilder templateBuilder = compute.templateBuilder();
-      if (!Strings.isNullOrEmpty(imageId)) {
-         LOGGER.info("Setting image id to " + imageId);
-         templateBuilder.imageId(imageId);
-      } else {
-         if (!Strings.isNullOrEmpty(osFamily)) {
-            LOGGER.info("Setting osFamily to " + osFamily);
-            templateBuilder.osFamily(OsFamily.valueOf(osFamily));
-         }
-         if (!Strings.isNullOrEmpty(osVersion)) {
-            LOGGER.info("Setting osVersion to " + osVersion);
-            templateBuilder.osVersionMatches(osVersion);
-         }
-      }
-      if (!Strings.isNullOrEmpty((hardwareId))) {
-         LOGGER.info("Setting hardware Id to " + hardwareId);
-      } else {
-         LOGGER.info("Setting minRam " + ram + " and minCores " + cores);
-         templateBuilder.minCores(cores).minRam(ram);
-      }
-
-      Template template = templateBuilder.build();
-
-      // setup the jcloudTemplate to customize the nodeMetadata with jdk, etc.
-      // also opening ports
-      AdminAccess adminAccess = AdminAccess.builder().adminUsername("jenkins").installAdminPrivateKey(false) // no
-                                                                                                             // need
-            .grantSudoToAdminUser(false) // no need
-            .adminPrivateKey(getCloud().privateKey) // temporary due to jclouds
-                                                    // bug
-            .authorizeAdminPublicKey(true).adminPublicKey(getCloud().publicKey).build();
-
-      // Jenkins needs /jenkins dir.
-      Statement jenkinsDirStatement = Statements.newStatementList(Statements.exec("mkdir /jenkins"),
-            Statements.exec("chown jenkins /jenkins"));
-
-      Statement bootstrap = newStatementList(adminAccess, jenkinsDirStatement, Statements.exec(this.initScript),
-            InstallJDK.fromOpenJDK());
-
-      template.getOptions().inboundPorts(22).userMetadata(userMetadata).runScript(bootstrap);
-
-      pooledCompute = new BasePooledComputeService(compute, "jenkins-pool", template, 10);
-
-      try {
-         pooledCompute.startPool();
-      } catch (RunNodesException e) {
-         destroyBadNodesAndPropagate(e);
-      }
-   }
-
-   private NodeMetadata createNodeWithJdk(String groupName) {
-      LOGGER.info("creating jclouds node");
-
-      NodeMetadata nodeMetadata = null;
-
-      try {
-         nodeMetadata = getOnlyElement(pooledCompute.createNodesInGroup(groupName, 1));
-      } catch (RunNodesException e) {
-         throw destroyBadNodesAndPropagate(e);
-      }
-
-      // Check if nodeMetadata is null and throw
-      return nodeMetadata;
-   }
-
-   private RuntimeException destroyBadNodesAndPropagate(RunNodesException e) {
-      for (Map.Entry<? extends NodeMetadata, ? extends Throwable> nodeError : e.getNodeErrors().entrySet())
-         getCloud().getCompute().destroyNode(nodeError.getKey().getId());
-      throw propagate(e);
-   }
-
-   public void terminate(NodeMetadata nodeMetaData) {
-      if (stopOnTerminate) {
-         LOGGER.info("Suspending the Slave : " + nodeMetaData.getName());
-         final ComputeService compute = getCloud().getCompute();
-         compute.suspendNode(nodeMetaData.getId());
-      } else {
-         LOGGER.info("Terminating the Slave : " + nodeMetaData.getName());
-         final ComputeService compute = getCloud().getCompute();
-         compute.destroyNode(nodeMetaData.getId());
-      }
-   }
-
-}
diff --git a/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceTest.java b/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceTest.java
new file mode 100644
index 0000000..2ac202a
--- /dev/null
+++ b/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceTest.java
@@ -0,0 +1,72 @@
+package org.jclouds.nodepool;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.predicates.NodePredicates;
+import org.jclouds.nodepool.internal.EagerNodePoolComputeService;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+@Test(groups = "unit", testName = "NodePoolComputeServiceTest")
+public class NodePoolComputeServiceTest {
+
+   private EagerNodePoolComputeService pooledComputeService;
+
+   @BeforeClass
+   public void setUp() {
+      ComputeServiceContext stubCtx = ContextBuilder.newBuilder("stub").buildView(ComputeServiceContext.class);
+      this.pooledComputeService = new EagerNodePoolComputeService(stubCtx, "pool", 10, 5, true, stubCtx
+               .getComputeService().templateBuilder().build(), stubCtx.utils().getUserExecutor());
+   }
+
+   public void testStartPool() throws InterruptedException, ExecutionException {
+      ListenableFuture<Void> future = this.pooledComputeService.startPool();
+      future.get();
+      assertEquals(pooledComputeService.ready(), 5);
+      assertEquals(pooledComputeService.size(), 5);
+      assertEquals(pooledComputeService.maxSize(), 10);
+   }
+
+   @Test(dependsOnMethods = "testStartPool", groups = { "unit", "poolStarted" })
+   public void testAllocateMinNodes() throws RunNodesException {
+      this.pooledComputeService.createNodesInGroup("1", 5);
+      // this pool is not supposed to add nodes past min until we request them
+      assertEquals(pooledComputeService.ready(), 0);
+      assertEquals(pooledComputeService.size(), 5);
+   }
+
+   @Test(dependsOnMethods = "testAllocateMinNodes", groups = { "unit", "poolStarted" })
+   public void testAllocateUpToMaxNodes() throws RunNodesException {
+      this.pooledComputeService.createNodesInGroup("2", 5);
+      assertEquals(pooledComputeService.ready(), 0);
+      assertEquals(pooledComputeService.size(), 10);
+   }
+
+   @Test(dependsOnMethods = "testAllocateUpToMaxNodes", groups = { "unit", "poolStarted" }, expectedExceptions = RunNodesException.class)
+   public void testAllocateMoreNodesFails() throws RunNodesException {
+      this.pooledComputeService.createNodesInGroup("3", 5);
+      System.out.println(this.pooledComputeService.size());
+   }
+
+   @Test(dependsOnMethods = "testAllocateUpToMaxNodes", groups = { "unit", "poolStarted" })
+   public void testDeallocatingNodesAndReallocating() throws RunNodesException {
+      this.pooledComputeService.destroyNodesMatching(NodePredicates.inGroup("2"));
+      assertEquals(pooledComputeService.ready(), 5);
+      this.pooledComputeService.createNodesInGroup("2", 5);
+   }
+
+   @Test(dependsOnGroups = "poolStarted")
+   public void testClose() throws IOException {
+      this.pooledComputeService.close();
+      assertEquals(0, pooledComputeService.ready());
+   }
+
+}
diff --git a/labs/nodepool/src/test/resources/logback.xml b/labs/nodepool/src/test/resources/logback.xml
new file mode 100644
index 0000000..fc22c87
--- /dev/null
+++ b/labs/nodepool/src/test/resources/logback.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>

+<configuration scan="false">

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

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

+

+    <encoder>

+      <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

+    </encoder>

+  </appender>

+

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

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

+

+    <encoder>

+      <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

+    </encoder>

+  </appender>

+

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

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

+

+    <encoder>

+      <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

+    </encoder>

+  </appender>

+

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

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

+

+    <encoder>

+      <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

+    </encoder>

+  </appender>

+

+  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">

+    <encoder>

+      <Pattern>%-4r [%thread] %-5level - %msg%n</Pattern>

+    </encoder>

+  </appender>

+

+  <root>

+    <level value="DEBUG" />

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

+  </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/labs/opsource-servers/pom.xml b/labs/opsource-servers/pom.xml
index 2f02745..71011b0 100644
--- a/labs/opsource-servers/pom.xml
+++ b/labs/opsource-servers/pom.xml
@@ -36,12 +36,10 @@
   <properties>
     <test.opsource-servers.endpoint>https://api.opsourcecloud.net/oec/${jclouds.api-version}</test.opsource-servers.endpoint>
     <test.opsource-servers.api-version>0.9</test.opsource-servers.api-version>
-    <test.opsource-servers.build-version />
+    <test.opsource-servers.build-version></test.opsource-servers.build-version>
     <test.opsource-servers.identity>FIXME_USERNAME</test.opsource-servers.identity>
     <test.opsource-servers.credential>FIXME_PASSWORD</test.opsource-servers.credential>
-    <test.opsource-servers.image-id />
-    <test.opsource-servers.image.login-user />
-    <test.opsource-servers.image.authenticate-sudo />
+    <test.opsource-servers.template></test.opsource-servers.template>
     <jclouds.osgi.export>org.jclouds.vcloud.director.v1_5*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -107,9 +105,7 @@
                     <test.opsource-servers.build-version>${test.opsource-servers.build-version}</test.opsource-servers.build-version>
                     <test.opsource-servers.identity>${test.opsource-servers.identity}</test.opsource-servers.identity>
                     <test.opsource-servers.credential>${test.opsource-servers.credential}</test.opsource-servers.credential>
-                    <test.opsource-servers.image-id>${test.opsource-servers.image-id}</test.opsource-servers.image-id>
-                    <test.opsource-servers.image.login-user>${test.opsource-servers.image.login-user}</test.opsource-servers.image.login-user>
-                    <test.opsource-servers.image.authenticate-sudo>${test.opsource-servers.image.authenticate-sudo}</test.opsource-servers.image.authenticate-sudo>
+                    <test.opsource-servers.template>${test.opsource-servers.template}</test.opsource-servers.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/labs/pom.xml b/labs/pom.xml
index 2c45d37..b223764 100644
--- a/labs/pom.xml
+++ b/labs/pom.xml
@@ -46,8 +46,11 @@
        <module>cdmi</module>
        <module>openstack-glance</module>
        <module>joyent-sdc</module>
+       <module>joyentcloud</module>
        <module>openstack-quantum</module>
        <module>rackspace-cloudidentity</module>
        <module>rackspace-cloudservers-us</module>
+       <module>greenqloud-compute</module>
+       <module>greenqloud-storage</module>
     </modules>
 </project>
diff --git a/labs/rackspace-cloudservers-us/pom.xml b/labs/rackspace-cloudservers-us/pom.xml
index ccb4c21..cea349d 100644
--- a/labs/rackspace-cloudservers-us/pom.xml
+++ b/labs/rackspace-cloudservers-us/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.rackspace-cloudservers-us.endpoint>https://identity.api.rackspacecloud.com</test.rackspace-cloudservers-us.endpoint>
     <test.rackspace-cloudservers-us.api-version>2</test.rackspace-cloudservers-us.api-version>
-    <test.rackspace-cloudservers-us.build-version />
+    <test.rackspace-cloudservers-us.build-version></test.rackspace-cloudservers-us.build-version>
     <test.rackspace-cloudservers-us.identity>${test.rackspace-us.identity}</test.rackspace-cloudservers-us.identity>
     <test.rackspace-cloudservers-us.credential>${test.rackspace-us.credential}</test.rackspace-cloudservers-us.credential>
-    <test.rackspace-cloudservers-us.image-id />
-    <test.rackspace-cloudservers-us.image.login-user />
-    <test.rackspace-cloudservers-us.image.authenticate-sudo />
-
+    <test.rackspace-cloudservers-us.template></test.rackspace-cloudservers-us.template>
     <jclouds.osgi.export>org.jclouds.rackspace.cloudservers.us*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -134,9 +131,7 @@
                     <test.rackspace-cloudservers-us.build-version>${test.rackspace-cloudservers-us.build-version}</test.rackspace-cloudservers-us.build-version>
                     <test.rackspace-cloudservers-us.identity>${test.rackspace-cloudservers-us.identity}</test.rackspace-cloudservers-us.identity>
                     <test.rackspace-cloudservers-us.credential>${test.rackspace-cloudservers-us.credential}</test.rackspace-cloudservers-us.credential>
-                    <test.rackspace-cloudservers-us.image-id>${test.rackspace-cloudservers-us.image-id}</test.rackspace-cloudservers-us.image-id>
-                    <test.rackspace-cloudservers-us.image.login-user>${test.rackspace-cloudservers-us.image.login-user}</test.rackspace-cloudservers-us.image.login-user>
-                    <test.rackspace-cloudservers-us.image.authenticate-sudo>${test.rackspace-cloudservers-us.image.authenticate-sudo}</test.rackspace-cloudservers-us.image.authenticate-sudo>
+                    <test.rackspace-cloudservers-us.template>${test.rackspace-cloudservers-us.template}</test.rackspace-cloudservers-us.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/labs/savvis-symphonyvpdc/pom.xml b/labs/savvis-symphonyvpdc/pom.xml
index a7013fd..c69a761 100644
--- a/labs/savvis-symphonyvpdc/pom.xml
+++ b/labs/savvis-symphonyvpdc/pom.xml
@@ -39,7 +39,7 @@
         <test.savvis-symphonyvpdc.build-version>2.3</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 />
+        <test.savvis-symphonyvpdc.template></test.savvis-symphonyvpdc.template>
         <test.savvis-symphonyvpdc.vdc-email>FIXME</test.savvis-symphonyvpdc.vdc-email>
         <test.savvis-symphonyvpdc.loginUser>FIXME</test.savvis-symphonyvpdc.loginUser>
         <test.savvis-symphonyvpdc.loginPassword>FIXME</test.savvis-symphonyvpdc.loginPassword>
@@ -112,7 +112,7 @@
                                         <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>
+                                        <test.savvis-symphonyvpdc.template>${test.savvis-symphonyvpdc.template}</test.savvis-symphonyvpdc.template>
                                         <test.savvis-symphonyvpdc.vdc-email>${test.savvis-symphonyvpdc.vdc-email}</test.savvis-symphonyvpdc.vdc-email>
                                         <test.savvis-symphonyvpdc.loginUser>${test.savvis-symphonyvpdc.loginUser}</test.savvis-symphonyvpdc.loginUser>
                                         <test.savvis-symphonyvpdc.loginPassword>${test.savvis-symphonyvpdc.loginPassword}</test.savvis-symphonyvpdc.loginPassword>
diff --git a/labs/vcloud-director/pom.xml b/labs/vcloud-director/pom.xml
index e2bad9b..27a8807 100644
--- a/labs/vcloud-director/pom.xml
+++ b/labs/vcloud-director/pom.xml
@@ -39,15 +39,13 @@
     <test.vcloud-director.build-version>1.5.0.464915</test.vcloud-director.build-version>
     <test.vcloud-director.identity>FIXME_USERNAME_WHICH_MIGHT_BE_EMAIL@JClouds</test.vcloud-director.identity>
     <test.vcloud-director.credential>FIXME_PASSWORD</test.vcloud-director.credential>
-    <test.vcloud-director.image-id />
-    <test.vcloud-director.image.login-user />
-    <test.vcloud-director.image.authenticate-sudo />
-    <test.vcloud-director.catalog-id />
-    <test.vcloud-director.media-id />
-    <test.vcloud-director.vapptemplate-id />
-    <test.vcloud-director.network-id />
-    <test.vcloud-director.vdc-id />
-    <test.vcloud-director.user-id />
+    <test.vcloud-director.template></test.vcloud-director.template>
+    <test.vcloud-director.catalog-id></test.vcloud-director.catalog-id>
+    <test.vcloud-director.media-id></test.vcloud-director.media-id>
+    <test.vcloud-director.vapptemplate-id></test.vcloud-director.vapptemplate-id>
+    <test.vcloud-director.network-id></test.vcloud-director.network-id>
+    <test.vcloud-director.vdc-id></test.vcloud-director.vdc-id>
+    <test.vcloud-director.user-id></test.vcloud-director.user-id>
 
     <jclouds.osgi.export>org.jclouds.vcloud.director.v1_5*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
@@ -125,9 +123,7 @@
                     <test.vcloud-director.build-version>${test.vcloud-director.build-version}</test.vcloud-director.build-version>
                     <test.vcloud-director.identity>${test.vcloud-director.identity}</test.vcloud-director.identity>
                     <test.vcloud-director.credential>${test.vcloud-director.credential}</test.vcloud-director.credential>
-                    <test.vcloud-director.image-id>${test.vcloud-director.image-id}</test.vcloud-director.image-id>
-                    <test.vcloud-director.image.login-user>${test.vcloud-director.image.login-user}</test.vcloud-director.image.login-user>
-                    <test.vcloud-director.image.authenticate-sudo>${test.vcloud-director.image.authenticate-sudo}</test.vcloud-director.image.authenticate-sudo>
+                    <test.vcloud-director.template>${test.vcloud-director.template}</test.vcloud-director.template>
                     <test.vcloud-director.catalog-id>${test.vcloud-director.catalog-id}</test.vcloud-director.catalog-id>
                     <test.vcloud-director.media-id>${test.vcloud-director.media-id}</test.vcloud-director.media-id>
                     <test.vcloud-director.vapptemplate-id>${test.vcloud-director.vapptemplate-id}</test.vcloud-director.vapptemplate-id>
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientLiveTest.java
index 53e8512..accbef2 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientLiveTest.java
@@ -408,7 +408,7 @@
                      orgRef.getHref(), restorableSettings);
          } catch (Exception e) {
             if (exception != null) {
-               logger.warn(e, "Error reseting settings; rethrowing origina test exception...");
+               logger.warn(e, "Error resetting settings; rethrowing original test exception...");
                throw exception;
             } else {
                throw e;
diff --git a/labs/virtualbox/pom.xml b/labs/virtualbox/pom.xml
index 01e75cb..4355f52 100644
--- a/labs/virtualbox/pom.xml
+++ b/labs/virtualbox/pom.xml
@@ -39,10 +39,7 @@
     <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-ubuntu-11.10-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>
-
+    <test.virtualbox.template>imageId=test-ubuntu-11.10-i386,loginUser=toor:password,authenticateSudo=true</test.virtualbox.template>
     <jclouds.osgi.export>org.jclouds.virtualbox*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
   </properties>
@@ -144,9 +141,7 @@
                     <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>
-                    <test.virtualbox.image.login-user>${test.virtualbox.image.login-user}</test.virtualbox.image.login-user>
-                    <test.virtualbox.image.authenticate-sudo>${test.virtualbox.image.authenticate-sudo}</test.virtualbox.image.authenticate-sudo>
+                    <test.virtualbox.template>${test.virtualbox.template}</test.virtualbox.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java
index 1f7915f..b2c2c45 100644
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java
@@ -139,8 +139,7 @@
       Template template = view.getComputeService().templateBuilder().build();
       checkNotNull(mastersCache.apply(template.getImage()));
 
-      imageId = template.getImage().getId();
-      masterVmName = VIRTUALBOX_IMAGE_PREFIX + imageId;
+      masterVmName = VIRTUALBOX_IMAGE_PREFIX + template.getImage().getId();
       isosDir = workingDir + File.separator + "isos";
 
       hostVersion = Iterables.get(Splitter.on('r').split(view.utils().injector().getInstance(Key.get(String.class, BuildVersion.class))), 0);
@@ -174,7 +173,7 @@
    }
 
    public MasterSpec getMasterSpecForTest() {
-      String masterName = "jclouds-image-0x0-" + imageId;
+      String masterName = "jclouds-image-0x0-" + template.getImageId();
 
       StorageController ideController = StorageController
                .builder()
diff --git a/providers/aws-ec2/pom.xml b/providers/aws-ec2/pom.xml
index 1ffffde..3ea8981 100644
--- a/providers/aws-ec2/pom.xml
+++ b/providers/aws-ec2/pom.xml
@@ -36,13 +36,11 @@
   <properties>
     <test.aws-ec2.endpoint>https://ec2.us-east-1.amazonaws.com</test.aws-ec2.endpoint>
     <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.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 />
-    <test.aws-ec2.image.login-user />
-    <test.aws-ec2.image.authenticate-sudo />
-
+    <test.aws-ec2.template></test.aws-ec2.template>
+    <test.aws-ec2.ebs-template>hardwareId=m1.small,imageId=us-west-2/ami-38c64a08</test.aws-ec2.ebs-template>
     <jclouds.osgi.export>org.jclouds.aws.ec2*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -137,9 +135,8 @@
                     <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>
-                    <test.aws-ec2.image.login-user>${test.aws-ec2.image.login-user}</test.aws-ec2.image.login-user>
-                    <test.aws-ec2.image.authenticate-sudo>${test.aws-ec2.image.authenticate-sudo}</test.aws-ec2.image.authenticate-sudo>
+                    <test.aws-ec2.template>${test.aws-ec2.template}</test.aws-ec2.template>
+                    <test.aws-ec2.ebs-template>${test.aws-ec2.ebs-template}</test.aws-ec2.ebs-template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
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
deleted file mode 100644
index 9cf363c..0000000
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AMIClientLiveTest.java
+++ /dev/null
@@ -1,275 +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.aws.ec2.services;
-
-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;
-import static org.jclouds.ec2.options.RegisterImageOptions.Builder.withDescription;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-
-import java.util.Iterator;
-import java.util.Set;
-
-import org.jclouds.aws.domain.Region;
-import org.jclouds.aws.ec2.AWSEC2ApiMetadata;
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
-import org.jclouds.compute.predicates.ImagePredicates;
-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.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeClass;
-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;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-
-/**
- * Tests behavior of {@code AMIClient}
- * 
- * @author Adrian Cole
- */
-@Test(groups = "live", singleThreaded = true)
-public class AMIClientLiveTest extends BaseComputeServiceContextLiveTest {
-   public AMIClientLiveTest() {
-      provider = "aws-ec2";
-      // TODO: parameterize this.
-      imageId = "ami-cdf819a4";
-   }
-   
-   private AMIClient client;
-   private static final String DEFAULT_MANIFEST = "adrianimages/image.manifest.xml";
-   private static final String DEFAULT_SNAPSHOT = "TODO";
-
-   private Set<String> imagesToDeregister = Sets.newHashSet();
-   private Set<String> snapshotsToDelete = Sets.newHashSet();
-
-   @Override
-   @BeforeClass(groups = { "integration", "live" })
-   public void setupContext() {
-      super.setupContext();
-      client = view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getAMIServices();
-   }
-
-   public void testDescribeImageNotExists() {
-      assertEquals(client.describeImagesInRegion(null, imageIds("ami-cdf819a3")).size(), 0);
-   }
-
-   @Test(expectedExceptions = IllegalArgumentException.class)
-   public void testDescribeImageBadId() {
-      client.describeImagesInRegion(null, imageIds("asdaasdsa"));
-   }
-
-   public void testDescribeImages() {
-      for (String region : view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getAvailabilityZoneAndRegionServices().describeRegions().keySet()) {
-         Set<? extends Image> allResults = client.describeImagesInRegion(region);
-         assertNotNull(allResults);
-         assert allResults.size() >= 2 : allResults.size();
-         Iterator<? extends Image> iterator = allResults.iterator();
-         String id1 = iterator.next().getId();
-         String id2 = iterator.next().getId();
-         Set<? extends Image> twoResults = client.describeImagesInRegion(region, imageIds(id1, id2));
-         assertNotNull(twoResults);
-         assertEquals(twoResults.size(), 2);
-         iterator = twoResults.iterator();
-         assertEquals(iterator.next().getId(), id1);
-         assertEquals(iterator.next().getId(), id2);
-      }
-   }
-
-   public void testDescribeImagesCC() {
-      Set<? extends Image> ccResults = client.describeImagesInRegion(Region.US_EAST_1, filters(
-               ImmutableMultimap.<String, String> builder()//
-                        .put("virtualization-type", "hvm")//
-                        .put("architecture", "x86_64")//
-                        .putAll("owner-id", ImmutableSet.<String> of("137112412989", "099720109477"))//
-                        .put("hypervisor", "xen")//
-                        .put("state", "available")//
-                        .put("image-type", "machine")//
-                        .put("root-device-type", "ebs")//
-                        .build()).ownedBy("137112412989", "099720109477"));
-      assertNotNull(ccResults);
-      assert (ccResults.size() >= 34) : ccResults;
-   }
-
-   @Test(enabled = false)
-   public void testRegisterImageFromManifest() {
-      String imageRegisteredId = client.registerImageFromManifestInRegion(null, "jcloudstest1", DEFAULT_MANIFEST);
-      imagesToDeregister.add(imageRegisteredId);
-      Image imageRegisteredFromManifest = Iterables.getOnlyElement(client.describeImagesInRegion(null,
-               imageIds(imageRegisteredId)));
-      assertEquals(imageRegisteredFromManifest.getName(), "jcloudstest1");
-      assertEquals(imageRegisteredFromManifest.getImageLocation(), DEFAULT_MANIFEST);
-      assertEquals(imageRegisteredFromManifest.getImageType(), ImageType.MACHINE);
-      assertEquals(imageRegisteredFromManifest.getRootDeviceType(), RootDeviceType.INSTANCE_STORE);
-      assertEquals(imageRegisteredFromManifest.getRootDeviceName(), "/dev/sda1");
-   }
-
-   @Test(enabled = false)
-   public void testRegisterImageFromManifestOptions() {
-      String imageRegisteredWithOptionsId = client.registerImageFromManifestInRegion(null, "jcloudstest2",
-               DEFAULT_MANIFEST, withDescription("adrian"));
-      imagesToDeregister.add(imageRegisteredWithOptionsId);
-      Image imageRegisteredFromManifestWithOptions = Iterables.getOnlyElement(client.describeImagesInRegion(null,
-               imageIds(imageRegisteredWithOptionsId)));
-      assertEquals(imageRegisteredFromManifestWithOptions.getName(), "jcloudstest2");
-      assertEquals(imageRegisteredFromManifestWithOptions.getImageLocation(), DEFAULT_MANIFEST);
-      assertEquals(imageRegisteredFromManifestWithOptions.getImageType(), ImageType.MACHINE);
-      assertEquals(imageRegisteredFromManifestWithOptions.getRootDeviceType(), RootDeviceType.INSTANCE_STORE);
-      assertEquals(imageRegisteredFromManifestWithOptions.getRootDeviceName(), "/dev/sda1");
-      assertEquals(imageRegisteredFromManifestWithOptions.getDescription(), "adrian");
-   }
-
-   @Test
-   public void testNewlyRegisteredImageCanBeListed() throws Exception {
-      ComputeService computeService = view.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(view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getInstanceServices().describeInstancesInRegion(null, instanceId));
-         RunningInstance instance = Iterables.getOnlyElement(reservation);
-         BlockDevice device = instance.getEbsBlockDevices().get("/dev/sda1");
-         Snapshot snapshot = view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).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() {
-      String imageRegisteredId = client.registerUnixImageBackedByEbsInRegion(null, "jcloudstest1", DEFAULT_MANIFEST);
-      imagesToDeregister.add(imageRegisteredId);
-      Image imageRegistered = Iterables
-               .getOnlyElement(client.describeImagesInRegion(null, imageIds(imageRegisteredId)));
-      assertEquals(imageRegistered.getName(), "jcloudstest1");
-      assertEquals(imageRegistered.getImageType(), ImageType.MACHINE);
-      assertEquals(imageRegistered.getRootDeviceType(), RootDeviceType.EBS);
-      assertEquals(imageRegistered.getRootDeviceName(), "/dev/sda1");
-   }
-
-   @Test(enabled = false)
-   // awaiting EBS functionality to be added to jclouds
-   public void testRegisterImageBackedByEBSOptions() {
-      String imageRegisteredWithOptionsId = client.registerUnixImageBackedByEbsInRegion(null, "jcloudstest2",
-               DEFAULT_SNAPSHOT, addNewBlockDevice("/dev/sda2", "myvirtual", 1).withDescription("adrian"));
-      imagesToDeregister.add(imageRegisteredWithOptionsId);
-      Image imageRegisteredWithOptions = Iterables.getOnlyElement(client.describeImagesInRegion(null,
-               imageIds(imageRegisteredWithOptionsId)));
-      assertEquals(imageRegisteredWithOptions.getName(), "jcloudstest2");
-      assertEquals(imageRegisteredWithOptions.getImageType(), ImageType.MACHINE);
-      assertEquals(imageRegisteredWithOptions.getRootDeviceType(), RootDeviceType.EBS);
-      assertEquals(imageRegisteredWithOptions.getRootDeviceName(), "/dev/sda1");
-      assertEquals(imageRegisteredWithOptions.getDescription(), "adrian");
-      assertEquals(imageRegisteredWithOptions.getEbsBlockDevices().entrySet(), ImmutableMap.of("/dev/sda1",
-               new Image.EbsBlockDevice("/dev/sda1", 30, true), "/dev/sda2",
-               new Image.EbsBlockDevice("/dev/sda2", 1, true)).entrySet());
-   }
-
-   @Test(enabled = false)
-   public void testCreateImage() {
-      // TODO client.createImageInRegion(null, name, instanceId, options);
-   }
-
-   @Test(enabled = false)
-   public void testAddProductCodesToImage() {
-      // TODO client.addProductCodesToImageInRegion(null, productCodes, imageId);
-   }
-
-   @Test(enabled = false)
-   public void testAddLaunchPermissionsToImage() {
-      // TODO client.addLaunchPermissionsToImageInRegion(null, userIds, userGroups,
-      // imageId);
-   }
-
-   @Test(enabled = false)
-   public void testRemoveLaunchPermissionsFromImage() {
-      // TODO client.removeLaunchPermissionsFromImageInRegion(null, userIds, userGroups,
-      // imageId);
-   }
-
-   @Test(enabled = false)
-   public void testResetLaunchPermissionsOnImage() {
-      // TODO client.resetLaunchPermissionsOnImageInRegion(null, imageId);
-   }
-
-   @Test(enabled = false)
-   public void testGetLaunchPermissionForImage() {
-      System.out.println(client.getLaunchPermissionForImageInRegion(null, imageId));
-   }
-
-   @Test(enabled = false)
-   // awaiting ebs support
-   public void testGetBlockDeviceMappingsForImage() {
-      System.out.println(client.getBlockDeviceMappingsForImageInRegion(null, imageId));
-   }
-
-   @AfterTest
-   public void cleanUp() {
-      for (String imageId : imagesToDeregister)
-         client.deregisterImageInRegion(null, imageId);
-      for (String snapshotId : snapshotsToDelete)
-         view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).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 739e843..fdab925 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,186 +18,43 @@
  */
 package org.jclouds.aws.ec2.services;
 
-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;
-import static org.testng.Assert.assertEquals;
+import static org.jclouds.aws.ec2.options.AWSDescribeImagesOptions.Builder.filters;
 import static org.testng.Assert.assertNotNull;
 
-import java.util.Iterator;
 import java.util.Set;
 
-import org.jclouds.aws.ec2.AWSEC2ApiMetadata;
-import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
+import org.jclouds.aws.domain.Region;
 import org.jclouds.ec2.domain.Image;
-import org.jclouds.ec2.domain.RootDeviceType;
-import org.jclouds.ec2.domain.Image.ImageType;
-import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeClass;
+import org.jclouds.ec2.services.AMIClientLiveTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
 
 /**
- * Tests behavior of {@code AWSAMIClient}
+ * Tests behavior of {@code AMIClient}
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", singleThreaded = true, testName = "AWSAMIClientLiveTest")
-public class AWSAMIClientLiveTest extends BaseComputeServiceContextLiveTest {
+@Test(groups = "live", singleThreaded = true)
+public class AWSAMIClientLiveTest extends AMIClientLiveTest {
+
    public AWSAMIClientLiveTest() {
       provider = "aws-ec2";
-      // TODO: parameterize this.
-      imageId = "ami-7ea24a17";
-   }
-   
-   private AWSAMIClient client;
-   private static final String DEFAULT_MANIFEST = "adrianimages/image.manifest.xml";
-   private static final String DEFAULT_SNAPSHOT = "TODO";
-
-   private Set<String> imagesToDeregister = Sets.newHashSet();
-
-   @Override
-   @BeforeClass(groups = { "integration", "live" })
-   public void setupContext() {
-      super.setupContext();
-      client = view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getAMIServices();
    }
 
-   public void testDescribeImageNotExists() {
-      assertEquals(client.describeImagesInRegion(null, imageIds("ami-cdf819a3")).size(), 0);
-   }
-
-   @Test(expectedExceptions = IllegalArgumentException.class)
-   public void testDescribeImageBadId() {
-      client.describeImagesInRegion(null, imageIds("asdaasdsa"));
-   }
-
-   public void testDescribeImages() {
-      for (String region : view.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi().getAvailabilityZoneAndRegionServices().describeRegions().keySet()) {
-         Set<Image> allResults = Sets.newLinkedHashSet(client.describeImagesInRegion(region));
-         assertNotNull(allResults);
-         assert allResults.size() >= 2 : allResults.size();
-         Iterator<Image> iterator = allResults.iterator();
-         String id1 = iterator.next().getId();
-         String id2 = iterator.next().getId();
-         Set<Image> twoResults = Sets.newLinkedHashSet(client.describeImagesInRegion(region, imageIds(id1, id2)));
-         assertNotNull(twoResults);
-         assertEquals(twoResults.size(), 2);
-         iterator = twoResults.iterator();
-         assertEquals(iterator.next().getId(), id1);
-         assertEquals(iterator.next().getId(), id2);
-      }
-   }
-
-   @Test(enabled = false)
-   public void testRegisterImageFromManifest() {
-      String imageRegisteredId = client.registerImageFromManifestInRegion(null, "jcloudstest1", DEFAULT_MANIFEST);
-      imagesToDeregister.add(imageRegisteredId);
-      Image imageRegisteredFromManifest = Iterables.getOnlyElement(client.describeImagesInRegion(null,
-            imageIds(imageRegisteredId)));
-      assertEquals(imageRegisteredFromManifest.getName(), "jcloudstest1");
-      assertEquals(imageRegisteredFromManifest.getImageLocation(), DEFAULT_MANIFEST);
-      assertEquals(imageRegisteredFromManifest.getImageType(), ImageType.MACHINE);
-      assertEquals(imageRegisteredFromManifest.getRootDeviceType(), RootDeviceType.INSTANCE_STORE);
-      assertEquals(imageRegisteredFromManifest.getRootDeviceName(), "/dev/sda1");
-   }
-
-   @Test(enabled = false)
-   public void testRegisterImageFromManifestOptions() {
-      String imageRegisteredWithOptionsId = client.registerImageFromManifestInRegion(null, "jcloudstest2",
-            DEFAULT_MANIFEST, withDescription("adrian"));
-      imagesToDeregister.add(imageRegisteredWithOptionsId);
-      Image imageRegisteredFromManifestWithOptions = Iterables.getOnlyElement(client.describeImagesInRegion(null,
-            imageIds(imageRegisteredWithOptionsId)));
-      assertEquals(imageRegisteredFromManifestWithOptions.getName(), "jcloudstest2");
-      assertEquals(imageRegisteredFromManifestWithOptions.getImageLocation(), DEFAULT_MANIFEST);
-      assertEquals(imageRegisteredFromManifestWithOptions.getImageType(), ImageType.MACHINE);
-      assertEquals(imageRegisteredFromManifestWithOptions.getRootDeviceType(), RootDeviceType.INSTANCE_STORE);
-      assertEquals(imageRegisteredFromManifestWithOptions.getRootDeviceName(), "/dev/sda1");
-      assertEquals(imageRegisteredFromManifestWithOptions.getDescription(), "adrian");
-   }
-
-   @Test(enabled = false)
-   // awaiting EBS functionality to be added to jclouds
-   public void testRegisterImageBackedByEBS() {
-      String imageRegisteredId = client.registerUnixImageBackedByEbsInRegion(null, "jcloudstest1", DEFAULT_MANIFEST);
-      imagesToDeregister.add(imageRegisteredId);
-      Image imageRegistered = Iterables
-            .getOnlyElement(client.describeImagesInRegion(null, imageIds(imageRegisteredId)));
-      assertEquals(imageRegistered.getName(), "jcloudstest1");
-      assertEquals(imageRegistered.getImageType(), ImageType.MACHINE);
-      assertEquals(imageRegistered.getRootDeviceType(), RootDeviceType.EBS);
-      assertEquals(imageRegistered.getRootDeviceName(), "/dev/sda1");
-   }
-
-   @Test(enabled = false)
-   // awaiting EBS functionality to be added to jclouds
-   public void testRegisterImageBackedByEBSOptions() {
-      String imageRegisteredWithOptionsId = client.registerUnixImageBackedByEbsInRegion(null, "jcloudstest2",
-            DEFAULT_SNAPSHOT, addNewBlockDevice("/dev/sda2", "myvirtual", 1).withDescription("adrian"));
-      imagesToDeregister.add(imageRegisteredWithOptionsId);
-      Image imageRegisteredWithOptions = Iterables.getOnlyElement(client.describeImagesInRegion(null,
-            imageIds(imageRegisteredWithOptionsId)));
-      assertEquals(imageRegisteredWithOptions.getName(), "jcloudstest2");
-      assertEquals(imageRegisteredWithOptions.getImageType(), ImageType.MACHINE);
-      assertEquals(imageRegisteredWithOptions.getRootDeviceType(), RootDeviceType.EBS);
-      assertEquals(imageRegisteredWithOptions.getRootDeviceName(), "/dev/sda1");
-      assertEquals(imageRegisteredWithOptions.getDescription(), "adrian");
-      assertEquals(
-            imageRegisteredWithOptions.getEbsBlockDevices().entrySet(),
-            ImmutableMap.of("/dev/sda1", new Image.EbsBlockDevice("/dev/sda1", 30, true), "/dev/sda2",
-                  new Image.EbsBlockDevice("/dev/sda2", 1, true)).entrySet());
-   }
-
-   @Test(enabled = false)
-   public void testCreateImage() {
-      // TODO client.createImageInRegion(null, name, instanceId, options);
-   }
-
-   @Test(enabled = false)
-   public void testAddProductCodesToImage() {
-      // TODO client.addProductCodesToImageInRegion(null, productCodes, imageId);
-   }
-
-   @Test(enabled = false)
-   public void testAddLaunchPermissionsToImage() {
-      // TODO client.addLaunchPermissionsToImageInRegion(null, userIds, userGroups,
-      // imageId);
-   }
-
-   @Test(enabled = false)
-   public void testRemoveLaunchPermissionsFromImage() {
-      // TODO client.removeLaunchPermissionsFromImageInRegion(null, userIds, userGroups,
-      // imageId);
-   }
-
-   @Test(enabled = false)
-   public void testResetLaunchPermissionsOnImage() {
-      // TODO client.resetLaunchPermissionsOnImageInRegion(null, imageId);
-   }
-
-   @Test(enabled = false)
-   public void testGetLaunchPermissionForImage() {
-      // TODO System.out.println(client.getLaunchPermissionForImageInRegion(null, imageId));
-   }
-
-   @Test(enabled = false)
-   public void testGetProductCodesForImage() {
-      // TODO System.out.println(client.getProductCodesForImageInRegion(null, imageId));
-   }
-
-   @Test(enabled = false)
-   // awaiting ebs support
-   public void testGetBlockDeviceMappingsForImage() {
-      System.out.println(client.getBlockDeviceMappingsForImageInRegion(null, imageId));
-   }
-
-   @AfterTest
-   public void deregisterImages() {
-      for (String imageId : imagesToDeregister)
-         client.deregisterImageInRegion(null, imageId);
+   public void testDescribeImagesCC() {
+      Set<? extends Image> ccResults = client.describeImagesInRegion(Region.US_EAST_1,
+            filters(ImmutableMultimap.<String, String> builder()//
+                  .put("virtualization-type", "hvm")//
+                  .put("architecture", "x86_64")//
+                  .putAll("owner-id", ImmutableSet.<String> of("137112412989", "099720109477"))//
+                  .put("hypervisor", "xen")//
+                  .put("state", "available")//
+                  .put("image-type", "machine")//
+                  .put("root-device-type", "ebs")//
+                  .build()).ownedBy("137112412989", "099720109477"));
+      assertNotNull(ccResults);
+      assert (ccResults.size() >= 34) : ccResults;
    }
 }
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSInstanceAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSInstanceAsyncClientTest.java
index dd0f9cd..8945da2 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSInstanceAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSInstanceAsyncClientTest.java
@@ -100,7 +100,7 @@
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
       assertSaxResponseParserClassEquals(method, InstanceStateChangeHandler.class);
-      assertExceptionParserClassEquals(method, null);
+      assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class);
 
       checkFilters(request);
    }
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/BaseAWSEC2AsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/BaseAWSEC2AsyncClientTest.java
index add38e4..b555713 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/BaseAWSEC2AsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/BaseAWSEC2AsyncClientTest.java
@@ -64,7 +64,7 @@
    protected static class StubAWSEC2RestClientModule extends AWSEC2RestClientModule {
 
       @Override
-      protected String provideTimeStamp(DateService dateService, int expiration) {
+      protected String provideTimeStamp(DateService dateService) {
          return "2009-11-08T15:54:08.897Z";
       }
 
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 74b2be2..63939d1 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
@@ -47,7 +47,7 @@
 import org.jclouds.scriptbuilder.statements.java.InstallJDK;
 import org.jclouds.scriptbuilder.statements.login.AdminAccess;
 import org.jclouds.sshj.config.SshjSshClientModule;
-import org.testng.annotations.AfterTest;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -163,12 +163,14 @@
 
    public static final String PREFIX = System.getProperty("user.name") + "ec2";
 
-   @AfterTest
-   public void shutdown() {
+   @Override
+   @AfterClass(groups = { "integration", "live" })
+   protected void tearDownContext() {
       if (group != null) {
          client.getPlacementGroupServices().deletePlacementGroupInRegion(group.getRegion(), group.getName());
          assert deletedTester.apply(group) : group;
       }
+      super.tearDownContext();
    }
    
    @Override
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
index bee6d98..e946df6 100644
--- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
@@ -225,7 +225,7 @@
                         }
                         String eTag = client.completeMultipartUpload(container, key, uploadId, etags);
                         logger.debug(String.format("multipart upload of %s to container %s with uploadId %s" +
-                        		" succeffully finished with %s retries", key, container, uploadId, errors.get()));
+                            " successfully finished with %s retries", key, container, uploadId, errors.get()));
                         return eTag;
                      } catch (Exception ex) {
                         RuntimeException rtex = Throwables2.getFirstThrowableOfType(ex, RuntimeException.class);
diff --git a/providers/bluelock-vcloud-zone01/pom.xml b/providers/bluelock-vcloud-zone01/pom.xml
index 354294d..68786b0 100644
--- a/providers/bluelock-vcloud-zone01/pom.xml
+++ b/providers/bluelock-vcloud-zone01/pom.xml
@@ -39,10 +39,7 @@
     <test.bluelock-vcloud-zone01.build-version>1.5.0.464915</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 />
-    <test.bluelock-vcloud-zone01.image.login-user />
-    <test.bluelock-vcloud-zone01.image.authenticate-sudo />
-
+    <test.bluelock-vcloud-zone01.template></test.bluelock-vcloud-zone01.template>
     <jclouds.osgi.export>org.jclouds.bluelock.vcloud.zone01*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -115,9 +112,7 @@
                     <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>
-                    <test.bluelock-vcloud-zone01.image.login-user>${test.bluelock-vcloud-zone01.image.login-user}</test.bluelock-vcloud-zone01.image.login-user>
-                    <test.bluelock-vcloud-zone01.image.authenticate-sudo>${test.bluelock-vcloud-zone01.image.authenticate-sudo}</test.bluelock-vcloud-zone01.image.authenticate-sudo>
+                    <test.bluelock-vcloud-zone01.template>${test.bluelock-vcloud-zone01.template}</test.bluelock-vcloud-zone01.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/providers/cloudservers-uk/pom.xml b/providers/cloudservers-uk/pom.xml
index dd0f694..771662c 100644
--- a/providers/cloudservers-uk/pom.xml
+++ b/providers/cloudservers-uk/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.cloudservers-uk.endpoint>https://lon.auth.api.rackspacecloud.com</test.cloudservers-uk.endpoint>
     <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.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 />
-    <test.cloudservers-uk.image.login-user />
-    <test.cloudservers-uk.image.authenticate-sudo />
-
+    <test.cloudservers-uk.template></test.cloudservers-uk.template>
     <jclouds.osgi.export>org.jclouds.rackspace.cloudservers*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -120,9 +117,7 @@
                     <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>
-                    <test.cloudservers-uk.image.login-user>${test.cloudservers-uk.image.login-user}</test.cloudservers-uk.image.login-user>
-                    <test.cloudservers-uk.image.authenticate-sudo>${test.cloudservers-uk.image.authenticate-sudo}</test.cloudservers-uk.image.authenticate-sudo>
+                    <test.cloudservers-uk.template>${test.cloudservers-uk.template}</test.cloudservers-uk.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/providers/cloudservers-us/pom.xml b/providers/cloudservers-us/pom.xml
index 7574d3f..70a3140 100644
--- a/providers/cloudservers-us/pom.xml
+++ b/providers/cloudservers-us/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.cloudservers-us.endpoint>https://auth.api.rackspacecloud.com</test.cloudservers-us.endpoint>
     <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.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 />
-    <test.cloudservers-us.image.login-user />
-    <test.cloudservers-us.image.authenticate-sudo />
-
+    <test.cloudservers-us.template></test.cloudservers-us.template>
     <jclouds.osgi.export>org.jclouds.rackspace.cloudservers*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -121,9 +118,7 @@
                     <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>
-                    <test.cloudservers-us.image.login-user>${test.cloudservers-us.image.login-user}</test.cloudservers-us.image.login-user>
-                    <test.cloudservers-us.image.authenticate-sudo>${test.cloudservers-us.image.authenticate-sudo}</test.cloudservers-us.image.authenticate-sudo>
+                    <test.cloudservers-us.template>${test.cloudservers-us.template}</test.cloudservers-us.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/providers/cloudsigma-lvs/pom.xml b/providers/cloudsigma-lvs/pom.xml
index ea087a9..46b4ff1 100644
--- a/providers/cloudsigma-lvs/pom.xml
+++ b/providers/cloudsigma-lvs/pom.xml
@@ -36,12 +36,10 @@
   <properties>
     <test.cloudsigma-lvs.endpoint>https://api.lvs.cloudsigma.com</test.cloudsigma-lvs.endpoint>
     <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.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 />
-    <test.cloudsigma-lvs.image.login-user />
-    <test.cloudsigma-lvs.image.authenticate-sudo />
+    <test.cloudsigma-lvs.template></test.cloudsigma-lvs.template>
     <jclouds.osgi.export>org.jclouds.cloudsigma*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -114,9 +112,7 @@
                     <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>
-                    <test.cloudsigma-lvs.image.login-user>${test.cloudsigma-lvs.image.login-user}</test.cloudsigma-lvs.image.login-user>
-                    <test.cloudsigma-lvs.image.authenticate-sudo>${test.cloudsigma-lvs.image.authenticate-sudo}</test.cloudsigma-lvs.image.authenticate-sudo>
+                    <test.cloudsigma-lvs.template>${test.cloudsigma-lvs.template}</test.cloudsigma-lvs.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/providers/cloudsigma-zrh/pom.xml b/providers/cloudsigma-zrh/pom.xml
index 1972503..a8d9b8d 100644
--- a/providers/cloudsigma-zrh/pom.xml
+++ b/providers/cloudsigma-zrh/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.cloudsigma-zrh.endpoint>https://api.zrh.cloudsigma.com</test.cloudsigma-zrh.endpoint>
     <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.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 />
-    <test.cloudsigma-zrh.image.login-user />
-    <test.cloudsigma-zrh.image.authenticate-sudo />
-
+    <test.cloudsigma-zrh.template></test.cloudsigma-zrh.template>
     <jclouds.osgi.export>org.jclouds.cloudsigma*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -115,9 +112,7 @@
                     <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>
-                    <test.cloudsigma-zrh.image.login-user>${test.cloudsigma-zrh.image.login-user}</test.cloudsigma-zrh.image.login-user>
-                    <test.cloudsigma-zrh.image.authenticate-sudo>${test.cloudsigma-zrh.image.authenticate-sudo}</test.cloudsigma-zrh.image.authenticate-sudo>
+                    <test.cloudsigma-zrh.template>${test.cloudsigma-zrh.template}</test.cloudsigma-zrh.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/providers/elastichosts-lax-p/pom.xml b/providers/elastichosts-lax-p/pom.xml
index f92bf52..ca0e7f5 100644
--- a/providers/elastichosts-lax-p/pom.xml
+++ b/providers/elastichosts-lax-p/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.elastichosts-lax-p.endpoint>https://api.lax-p.elastichosts.com</test.elastichosts-lax-p.endpoint>
         <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.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 />
+        <test.elastichosts-lax-p.template></test.elastichosts-lax-p.template>
 
         <jclouds.osgi.export>org.jclouds.elastichosts*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.elastichosts-lax-p.template>${test.elastichosts-lax-p.template}</test.elastichosts-lax-p.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/elastichosts-lon-b/pom.xml b/providers/elastichosts-lon-b/pom.xml
index c925e54..6acef22 100644
--- a/providers/elastichosts-lon-b/pom.xml
+++ b/providers/elastichosts-lon-b/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.elastichosts-lon-b.endpoint>https://api.lon-b.elastichosts.com</test.elastichosts-lon-b.endpoint>
         <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.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 />
+        <test.elastichosts-lon-b.template></test.elastichosts-lon-b.template>
 
         <jclouds.osgi.export>org.jclouds.elastichosts*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.elastichosts-lon-b.template>${test.elastichosts-lon-b.template}</test.elastichosts-lon-b.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/elastichosts-lon-p/pom.xml b/providers/elastichosts-lon-p/pom.xml
index 618a00b..4bc74dd 100644
--- a/providers/elastichosts-lon-p/pom.xml
+++ b/providers/elastichosts-lon-p/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.elastichosts-lon-p.endpoint>https://api.lon-p.elastichosts.com</test.elastichosts-lon-p.endpoint>
         <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.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 />
+        <test.elastichosts-lon-p.template></test.elastichosts-lon-p.template>
 
         <jclouds.osgi.export>org.jclouds.elastichosts*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.elastichosts-lon-p.template>${test.elastichosts-lon-p.template}</test.elastichosts-lon-p.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/elastichosts-sat-p/pom.xml b/providers/elastichosts-sat-p/pom.xml
index f918362..6061d89 100644
--- a/providers/elastichosts-sat-p/pom.xml
+++ b/providers/elastichosts-sat-p/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.elastichosts-sat-p.endpoint>https://api.sat-p.elastichosts.com</test.elastichosts-sat-p.endpoint>
         <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.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 />
+        <test.elastichosts-sat-p.template></test.elastichosts-sat-p.template>
 
         <jclouds.osgi.export>org.jclouds.elastichosts*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.elastichosts-sat-p.template>${test.elastichosts-sat-p.template}</test.elastichosts-sat-p.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/elastichosts-tor-p/pom.xml b/providers/elastichosts-tor-p/pom.xml
index 2fad95b..29e566c 100644
--- a/providers/elastichosts-tor-p/pom.xml
+++ b/providers/elastichosts-tor-p/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.elastichosts-tor-p.endpoint>https://api.tor-p.elastichosts.com</test.elastichosts-tor-p.endpoint>
         <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.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 />
+        <test.elastichosts-tor-p.template></test.elastichosts-tor-p.template>
 
         <jclouds.osgi.export>org.jclouds.elastichosts*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.elastichosts-tor-p.template>${test.elastichosts-tor-p.template}</test.elastichosts-tor-p.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/eucalyptus-partnercloud-ec2/pom.xml b/providers/eucalyptus-partnercloud-ec2/pom.xml
index b75965e..bd9374e 100644
--- a/providers/eucalyptus-partnercloud-ec2/pom.xml
+++ b/providers/eucalyptus-partnercloud-ec2/pom.xml
@@ -36,12 +36,11 @@
     <properties>
         <test.eucalyptus-partnercloud-ec2.endpoint>http://partnercloud.eucalyptus.com:8773/services/Eucalyptus</test.eucalyptus-partnercloud-ec2.endpoint>
         <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.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 />
-        <!--  corresponds to image manifest and also virt + "-cluster" zone -->
-        <test.eucalyptus-partnercloud-ec2.virtualization-type>kvm</test.eucalyptus-partnercloud-ec2.virtualization-type>
+        <test.eucalyptus-partnercloud-ec2.template></test.eucalyptus-partnercloud-ec2.template>
+        <test.eucalyptus-partnercloud-ec2.ebs-template></test.eucalyptus-partnercloud-ec2.ebs-template>
 
         <jclouds.osgi.export>org.jclouds.epc*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -128,8 +127,8 @@
                                         <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>
-                                        <test.eucalyptus-partnercloud-ec2.virtualization-type>${test.eucalyptus-partnercloud-ec2.virtualization-type}</test.eucalyptus-partnercloud-ec2.virtualization-type>
+                                        <test.eucalyptus-partnercloud-ec2.template>${test.eucalyptus-partnercloud-ec2.template}</test.eucalyptus-partnercloud-ec2.template>
+                                        <test.eucalyptus-partnercloud-ec2.ebs-template>${test.eucalyptus-partnercloud-ec2.ebs-template}</test.eucalyptus-partnercloud-ec2.ebs-template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/eucalyptus-partnercloud-ec2/src/main/java/org/jclouds/epc/EucalyptusPartnerCloudEC2ProviderMetadata.java b/providers/eucalyptus-partnercloud-ec2/src/main/java/org/jclouds/epc/EucalyptusPartnerCloudEC2ProviderMetadata.java
index 0ff2825..7687626 100644
--- a/providers/eucalyptus-partnercloud-ec2/src/main/java/org/jclouds/epc/EucalyptusPartnerCloudEC2ProviderMetadata.java
+++ b/providers/eucalyptus-partnercloud-ec2/src/main/java/org/jclouds/epc/EucalyptusPartnerCloudEC2ProviderMetadata.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.epc;
 
-import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
 import static org.jclouds.location.reference.LocationConstants.ISO3166_CODES;
 import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION;
 import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
@@ -65,10 +64,8 @@
 
    public static Properties defaultProperties() {
       Properties properties = new Properties();
-      properties.setProperty(PROPERTY_REGIONS, "Eucalyptus");
-      properties.setProperty(PROPERTY_REGION + ".Eucalyptus." + ISO3166_CODES, "US-CA");
-      properties.setProperty("eucalyptus-partnercloud-ec2.virtualization-type", "kvm");
-      properties.setProperty(TEMPLATE, "osFamily=UBUNTU,locationId=${eucalyptus-partnercloud-ec2.virtualization-type}-cluster");
+      properties.setProperty(PROPERTY_REGIONS, "eucalyptus");
+      properties.setProperty(PROPERTY_REGION + ".eucalyptus." + ISO3166_CODES, "US-CA");
       return properties;
    }
    
@@ -82,7 +79,7 @@
                             .defaultModules(ImmutableSet.<Class<? extends Module>>of(EC2RestClientModule.class, EC2ResolveImagesModule.class, EucalyptusPartnerCloudComputeServiceContextModule.class))
                             .build())
          .homepage(URI.create("http://www.eucalyptus.com/partners"))
-         .console(URI.create("https://partnercloud.eucalyptus.com:8443"))
+         .console(URI.create("https://eucalyptus.partner.eucalyptus.com"))
          .linkedServices("eucalyptus-partnercloud-ec2", "eucalyptus-partnercloud-s3")
          .iso3166Codes("US-CA")
          .endpoint("http://partnercloud.eucalyptus.com:8773/services/Eucalyptus")
diff --git a/providers/eucalyptus-partnercloud-ec2/src/test/java/org/jclouds/epc/compute/EucalyptusPartnerCloudEucalyptusComputeServiceLiveTest.java b/providers/eucalyptus-partnercloud-ec2/src/test/java/org/jclouds/epc/compute/EucalyptusPartnerCloudEucalyptusComputeServiceLiveTest.java
index 80607c2..242b9e4 100644
--- a/providers/eucalyptus-partnercloud-ec2/src/test/java/org/jclouds/epc/compute/EucalyptusPartnerCloudEucalyptusComputeServiceLiveTest.java
+++ b/providers/eucalyptus-partnercloud-ec2/src/test/java/org/jclouds/epc/compute/EucalyptusPartnerCloudEucalyptusComputeServiceLiveTest.java
@@ -18,8 +18,6 @@
  */
 package org.jclouds.epc.compute;
 
-import java.util.Properties;
-
 import org.jclouds.eucalyptus.compute.EucalyptusComputeServiceLiveTest;
 import org.testng.annotations.Test;
 
@@ -35,14 +33,4 @@
       // security groups must be <30 characters
       group = "eu";
    }
-
-   @Override
-   protected Properties setupProperties() {
-      Properties overrides = super.setupProperties();
-      if (System.getProperties().containsKey("test.eucalyptus-partnercloud-ec2.virtualization-type"))
-         overrides.setProperty("eucalyptus-partnercloud-ec2.virtualization-type", System
-                  .getProperty("test.eucalyptus-partnercloud-ec2.virtualization-type"));
-      return overrides;
-   }
-
 }
diff --git a/providers/eucalyptus-partnercloud-s3/pom.xml b/providers/eucalyptus-partnercloud-s3/pom.xml
index da8bb6b..00c0e2c 100644
--- a/providers/eucalyptus-partnercloud-s3/pom.xml
+++ b/providers/eucalyptus-partnercloud-s3/pom.xml
@@ -34,7 +34,7 @@
     <packaging>bundle</packaging>
 
     <properties>
-        <test.eucalyptus-partnercloud-s3.endpoint>http://partnercloud.eucalyptus.com:8773/services/Walrus</test.eucalyptus-partnercloud-s3.endpoint>
+        <test.eucalyptus-partnercloud-s3.endpoint>http://walrus.partner.eucalyptus.com:8773/services/Walrus</test.eucalyptus-partnercloud-s3.endpoint>
         <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.identity>FIXME_IDENTITY</test.eucalyptus-partnercloud-s3.identity>
diff --git a/providers/eucalyptus-partnercloud-s3/src/main/java/org/jclouds/epc/EucalyptusPartnerCloudS3ProviderMetadata.java b/providers/eucalyptus-partnercloud-s3/src/main/java/org/jclouds/epc/EucalyptusPartnerCloudS3ProviderMetadata.java
index 7be125d..f28e71f 100644
--- a/providers/eucalyptus-partnercloud-s3/src/main/java/org/jclouds/epc/EucalyptusPartnerCloudS3ProviderMetadata.java
+++ b/providers/eucalyptus-partnercloud-s3/src/main/java/org/jclouds/epc/EucalyptusPartnerCloudS3ProviderMetadata.java
@@ -60,10 +60,10 @@
 
    public static Properties defaultProperties() {
       Properties properties = new Properties();
-      properties.setProperty(PROPERTY_REGIONS, "Walrus");
+      properties.setProperty(PROPERTY_REGIONS, "walrus");
       properties.setProperty(PROPERTY_ISO3166_CODES, "US-CA");
-      properties.setProperty(PROPERTY_REGION + ".Walrus." + ISO3166_CODES, "US-CA");
-      properties.setProperty(PROPERTY_REGION + "." + "Walrus" + "." + ENDPOINT, "http://partnercloud.eucalyptus.com:8773/services/Walrus");
+      properties.setProperty(PROPERTY_REGION + ".walrus." + ISO3166_CODES, "US-CA");
+      properties.setProperty(PROPERTY_REGION + "." + "walrus" + "." + ENDPOINT, "http://walrus.partner.eucalyptus.com:8773/services/Walrus");
       return properties;
    }
    
@@ -74,10 +74,10 @@
          .name("Eucalyptus Partner Cloud (S3)")
          .apiMetadata(new WalrusApiMetadata())
          .homepage(URI.create("http://www.eucalyptus.com/partners"))
-         .console(URI.create("https://partnercloud.eucalyptus.com:8443"))
+         .console(URI.create("https://walrus.partner.eucalyptus.com"))
          .linkedServices("eucalyptus-partnercloud-ec2", "eucalyptus-partnercloud-s3")
          .iso3166Codes("US-CA")
-         .endpoint("http://partnercloud.eucalyptus.com:8773/services/Walrus")
+         .endpoint("http://walrus.partner.eucalyptus.com:8773/services/Walrus")
          .defaultProperties(EucalyptusPartnerCloudS3ProviderMetadata.defaultProperties());
       }
 
diff --git a/providers/eucalyptus-partnercloud-s3/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudWalrusClientLiveTest.java b/providers/eucalyptus-partnercloud-s3/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudWalrusClientLiveTest.java
index 812d259..442e4a9 100644
--- a/providers/eucalyptus-partnercloud-s3/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudWalrusClientLiveTest.java
+++ b/providers/eucalyptus-partnercloud-s3/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudWalrusClientLiveTest.java
@@ -26,7 +26,9 @@
  * 
  * @author Adrian Cole
  */
-@Test(groups = "live", sequential = true, testName = "EucalyptusPartnerCloudWalrusClientLiveTest")
+@Test(groups = "live", singleThreaded = true, testName = "EucalyptusPartnerCloudWalrusClientLiveTest")
 public class EucalyptusPartnerCloudWalrusClientLiveTest extends WalrusClientLiveTest {
-
+   public EucalyptusPartnerCloudWalrusClientLiveTest() {
+      provider = "eucalyptus-partnercloud-s3";
+   }
 }
diff --git a/providers/go2cloud-jhb1/pom.xml b/providers/go2cloud-jhb1/pom.xml
index 76e3b4c..81e9026 100644
--- a/providers/go2cloud-jhb1/pom.xml
+++ b/providers/go2cloud-jhb1/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.go2cloud-jhb1.endpoint>http://api.jhb1.go2cloud.co.za</test.go2cloud-jhb1.endpoint>
         <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.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 />
+        <test.go2cloud-jhb1.template></test.go2cloud-jhb1.template>
 
         <jclouds.osgi.export>org.jclouds.go2cloud*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.go2cloud-jhb1.template>${test.go2cloud-jhb1.template}</test.go2cloud-jhb1.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/gogrid/pom.xml b/providers/gogrid/pom.xml
index b92f143..b42e13c 100644
--- a/providers/gogrid/pom.xml
+++ b/providers/gogrid/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.gogrid.endpoint>https://api.gogrid.com/api</test.gogrid.endpoint>
         <test.gogrid.api-version>1.5</test.gogrid.api-version>
-        <test.gogrid.build-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 />
+        <test.gogrid.template></test.gogrid.template>
 
         <jclouds.osgi.export>org.jclouds.gogrid*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -106,7 +106,7 @@
                                         <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>
+                                        <test.gogrid.template>${test.gogrid.template}</test.gogrid.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
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 8db55df..39f0d2d 100644
--- a/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridLiveTestDisabled.java
+++ b/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridLiveTestDisabled.java
@@ -211,7 +211,7 @@
 
       Job latestJobFetched = Iterables.getOnlyElement(client.getJobServices().getJobsById(latestJobId));
 
-      assert latestJob.equals(latestJobFetched) : "Job and its reprentation found by ID don't match";
+      assert latestJob.equals(latestJobFetched) : "Job and its representation found by ID don't match";
 
       long[] idsOfAllJobs = new long[jobs.size()];
       int i = 0;
diff --git a/providers/greenhousedata-element-vcloud/pom.xml b/providers/greenhousedata-element-vcloud/pom.xml
index 25ef92a..640bcf0 100644
--- a/providers/greenhousedata-element-vcloud/pom.xml
+++ b/providers/greenhousedata-element-vcloud/pom.xml
@@ -39,10 +39,7 @@
         <test.greenhousedata-element-vcloud.build-version>1.5.0.464915</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 />
-        <test.greenhousedata-element-vcloud.image.login-user />
-        <test.greenhousedata-element-vcloud.image.authenticate-sudo />
-
+        <test.greenhousedata-element-vcloud.template></test.greenhousedata-element-vcloud.template>
         <jclouds.osgi.export>org.jclouds.greenhousedata.element.vcloud*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
           org.jclouds.compute.internal;version="${project.version}",
@@ -115,9 +112,7 @@
                                         <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>
-                                        <test.greenhousedata-element-vcloud.image.login-user>${test.greenhousedata-element-vcloud.image.login-user}</test.greenhousedata-element-vcloud.image.login-user>
-                                        <test.greenhousedata-element-vcloud.image.authenticate-sudo>${test.greenhousedata-element-vcloud.image.authenticate-sudo}</test.greenhousedata-element-vcloud.image.authenticate-sudo>
+                                        <test.greenhousedata-element-vcloud.template>${test.greenhousedata-element-vcloud.template}</test.greenhousedata-element-vcloud.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/hpcloud-compute/pom.xml b/providers/hpcloud-compute/pom.xml
index 68e0127..aaa4bfd 100644
--- a/providers/hpcloud-compute/pom.xml
+++ b/providers/hpcloud-compute/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.hpcloud-compute.endpoint>https://region-a.geo-1.identity.hpcloudsvc.com:35357</test.hpcloud-compute.endpoint>
     <test.hpcloud-compute.api-version>1.1</test.hpcloud-compute.api-version>
-    <test.hpcloud-compute.build-version />
+    <test.hpcloud-compute.build-version></test.hpcloud-compute.build-version>
     <test.hpcloud-compute.identity>FIXME_IDENTITY</test.hpcloud-compute.identity>
     <test.hpcloud-compute.credential>FIXME_CREDENTIAL</test.hpcloud-compute.credential>
-    <test.hpcloud-compute.image-id />
-    <test.hpcloud-compute.image.login-user />
-    <test.hpcloud-compute.image.authenticate-sudo />
-
+    <test.hpcloud-compute.template></test.hpcloud-compute.template>
     <jclouds.osgi.export>org.jclouds.hpcloud.compute*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -122,9 +119,7 @@
                     <test.hpcloud-compute.build-version>${test.hpcloud-compute.build-version}</test.hpcloud-compute.build-version>
                     <test.hpcloud-compute.identity>${test.hpcloud-compute.identity}</test.hpcloud-compute.identity>
                     <test.hpcloud-compute.credential>${test.hpcloud-compute.credential}</test.hpcloud-compute.credential>
-                    <test.hpcloud-compute.image-id>${test.hpcloud-compute.image-id}</test.hpcloud-compute.image-id>
-                    <test.hpcloud-compute.image.login-user>${test.hpcloud-compute.image.login-user}</test.hpcloud-compute.image.login-user>
-                    <test.hpcloud-compute.image.authenticate-sudo>${test.hpcloud-compute.image.authenticate-sudo}</test.hpcloud-compute.image.authenticate-sudo>
+                    <test.hpcloud-compute.template>${test.hpcloud-compute.template}</test.hpcloud-compute.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/providers/ninefold-compute/pom.xml b/providers/ninefold-compute/pom.xml
index 7874950..17ab38c 100644
--- a/providers/ninefold-compute/pom.xml
+++ b/providers/ninefold-compute/pom.xml
@@ -39,10 +39,7 @@
     <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>
-    <test.ninefold-compute.image.login-user>user:Password01</test.ninefold-compute.image.login-user>
-    <test.ninefold-compute.image.authenticate-sudo>true</test.ninefold-compute.image.authenticate-sudo>
-
+    <test.ninefold-compute.template>imageId=1215,loginUser=user:Password01,authenticateSudo=true</test.ninefold-compute.template>
     <jclouds.osgi.export>org.jclouds.ninefold.compute*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -116,9 +113,7 @@
                     <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>
-                    <test.ninefold-compute.image.login-user>${test.ninefold-compute.image.login-user}</test.ninefold-compute.image.login-user>
-                    <test.ninefold-compute.image.authenticate-sudo>${test.ninefold-compute.image.authenticate-sudo}</test.ninefold-compute.image.authenticate-sudo>
+                    <test.ninefold-compute.template>${test.ninefold-compute.template}</test.ninefold-compute.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/providers/ninefold-compute/src/test/java/org/jclouds/ninefold/compute/compute/NinefoldComputeTemplateBuilderLiveTest.java b/providers/ninefold-compute/src/test/java/org/jclouds/ninefold/compute/compute/NinefoldComputeTemplateBuilderLiveTest.java
index fcdb275..912727f 100644
--- a/providers/ninefold-compute/src/test/java/org/jclouds/ninefold/compute/compute/NinefoldComputeTemplateBuilderLiveTest.java
+++ b/providers/ninefold-compute/src/test/java/org/jclouds/ninefold/compute/compute/NinefoldComputeTemplateBuilderLiveTest.java
@@ -71,14 +71,14 @@
    @Test
    public void testDefaultTemplateBuilder() throws IOException {
       Template defaultTemplate = this.view.getComputeService().templateBuilder().build();
-      if (imageId == null) {
+      if (template == null) {
          assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
          assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "10.04");
          assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
          assertEquals(defaultTemplate.getLocation().getId(), "1");
          assertEquals(getCores(defaultTemplate.getHardware()), 1.0d);
       } else {
-         assertEquals(defaultTemplate.getImage(), this.view.getComputeService().templateBuilder().imageId(imageId)
+         assertEquals(defaultTemplate.getImage(), this.view.getComputeService().templateBuilder().from(template)
                .build().getImage());
       }
    }
diff --git a/providers/openhosting-east1/pom.xml b/providers/openhosting-east1/pom.xml
index bc6e40a..7e87b05 100644
--- a/providers/openhosting-east1/pom.xml
+++ b/providers/openhosting-east1/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.openhosting-east1.endpoint>https://api.east1.openhosting.com</test.openhosting-east1.endpoint>
         <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.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 />
+        <test.openhosting-east1.template></test.openhosting-east1.template>
 
         <jclouds.osgi.export>org.jclouds.openhosting*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.openhosting-east1.template>${test.openhosting-east1.template}</test.openhosting-east1.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/rimuhosting/pom.xml b/providers/rimuhosting/pom.xml
index c3ca7c2..7ecbb09 100644
--- a/providers/rimuhosting/pom.xml
+++ b/providers/rimuhosting/pom.xml
@@ -36,9 +36,9 @@
     <properties>
         <test.rimuhosting.endpoint>https://api.rimuhosting.com/r</test.rimuhosting.endpoint>
         <test.rimuhosting.api-version>1.0</test.rimuhosting.api-version>
-        <test.rimuhosting.build-version />
+        <test.rimuhosting.build-version></test.rimuhosting.build-version>
         <test.rimuhosting.identity>FIXME</test.rimuhosting.identity>
-        <test.rimuhosting.image-id />
+        <test.rimuhosting.template></test.rimuhosting.template>
 
         <jclouds.osgi.export>org.jclouds.rimuhosting.miro*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.rimuhosting.template>${test.rimuhosting.template}</test.rimuhosting.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/data/NewServerData.java b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/data/NewServerData.java
index 762cdb8..0d8a155 100644
--- a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/data/NewServerData.java
+++ b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/data/NewServerData.java
@@ -107,7 +107,7 @@
     * stops them all fitting in 'neatly' (that's not a problem on the bigger-, non-low
     * contention-plans.
     */
-   @SerializedName("vps_paramters")
+   @SerializedName("vps_parameters")
    private ServerParameters serverParameters;
 
    @SerializedName("meta_data")
diff --git a/providers/serverlove-z1-man/pom.xml b/providers/serverlove-z1-man/pom.xml
index 3bd52cd..4b0ef8a 100644
--- a/providers/serverlove-z1-man/pom.xml
+++ b/providers/serverlove-z1-man/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.serverlove-z1-man.endpoint>https://api.z1-man.serverlove.com</test.serverlove-z1-man.endpoint>
         <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.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 />
+        <test.serverlove-z1-man.template></test.serverlove-z1-man.template>
         <jclouds.osgi.export>org.jclouds.serverlove*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
           org.jclouds.compute.internal;version="${project.version}",
@@ -112,7 +112,7 @@
                                         <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>
+                                        <test.serverlove-z1-man.template>${test.serverlove-z1-man.template}</test.serverlove-z1-man.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/skalicloud-sdg-my/pom.xml b/providers/skalicloud-sdg-my/pom.xml
index d0aa2af..e1b38fa 100644
--- a/providers/skalicloud-sdg-my/pom.xml
+++ b/providers/skalicloud-sdg-my/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.skalicloud-sdg-my.endpoint>https://api.sdg-my.skalicloud.com</test.skalicloud-sdg-my.endpoint>
         <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.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 />
+        <test.skalicloud-sdg-my.template></test.skalicloud-sdg-my.template>
 
         <jclouds.osgi.export>org.jclouds.skalicloud*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -113,7 +113,7 @@
                                         <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>
+                                        <test.skalicloud-sdg-my.template>${test.skalicloud-sdg-my.template}</test.skalicloud-sdg-my.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/slicehost/pom.xml b/providers/slicehost/pom.xml
index dc8167e..80f8a67 100644
--- a/providers/slicehost/pom.xml
+++ b/providers/slicehost/pom.xml
@@ -36,9 +36,9 @@
     <properties>
         <test.slicehost.endpoint>https://api.slicehost.com</test.slicehost.endpoint>
         <test.slicehost.api-version>1.4.1.1</test.slicehost.api-version>
-        <test.slicehost.build-version />
+        <test.slicehost.build-version></test.slicehost.build-version>
         <test.slicehost.identity>FIXME</test.slicehost.identity>
-        <test.slicehost.image-id />
+        <test.slicehost.template></test.slicehost.template>
 
         <jclouds.osgi.export>org.jclouds.slicehost*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -105,7 +105,7 @@
                                         <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>
+                                        <test.slicehost.template>${test.slicehost.template}</test.slicehost.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/softlayer/pom.xml b/providers/softlayer/pom.xml
index 0836c5e..9ddb6e2 100644
--- a/providers/softlayer/pom.xml
+++ b/providers/softlayer/pom.xml
@@ -50,13 +50,10 @@
   <properties>
     <test.softlayer.endpoint>https://api.softlayer.com/rest</test.softlayer.endpoint>
     <test.softlayer.api-version>3</test.softlayer.api-version>
-    <test.softlayer.build-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 />
-    <test.softlayer.image.login-user />
-    <test.softlayer.image.authenticate-sudo />
-
+    <test.softlayer.template></test.softlayer.template>
     <jclouds.osgi.export>org.jclouds.softlayer*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -120,9 +117,7 @@
                     <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>
-                    <test.softlayer.image.login-user>${test.softlayer.image.login-user}</test.softlayer.image.login-user>
-                    <test.softlayer.image.authenticate-sudo>${test.softlayer.image.authenticate-sudo}</test.softlayer.image.authenticate-sudo>
+                    <test.softlayer.template>${test.softlayer.template}</test.softlayer.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/providers/stratogen-vcloud-mycloud/pom.xml b/providers/stratogen-vcloud-mycloud/pom.xml
index cfa68f8..5f80ed3 100644
--- a/providers/stratogen-vcloud-mycloud/pom.xml
+++ b/providers/stratogen-vcloud-mycloud/pom.xml
@@ -39,10 +39,7 @@
         <test.stratogen-vcloud-mycloud.build-version>1.5.0.464915</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 />
-        <test.stratogen-vcloud-mycloud.image.login-user />
-        <test.stratogen-vcloud-mycloud.image.authenticate-sudo />
-
+        <test.stratogen-vcloud-mycloud.template></test.stratogen-vcloud-mycloud.template>
         <jclouds.osgi.export>org.jclouds.stratogen.vcloud.mycloud*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
           org.jclouds.compute.internal;version="${project.version}",
@@ -115,9 +112,7 @@
                                         <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>
-                                        <test.stratogen-vcloud-mycloud.image.login-user>${test.stratogen-vcloud-mycloud.image.login-user}</test.stratogen-vcloud-mycloud.image.login-user>
-                                        <test.stratogen-vcloud-mycloud.image.authenticate-sudo>${test.stratogen-vcloud-mycloud.image.authenticate-sudo}</test.stratogen-vcloud-mycloud.image.authenticate-sudo>
+                                        <test.stratogen-vcloud-mycloud.template>${test.stratogen-vcloud-mycloud.template}</test.stratogen-vcloud-mycloud.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/trmk-ecloud/pom.xml b/providers/trmk-ecloud/pom.xml
index 3eb59ff..f38b244 100644
--- a/providers/trmk-ecloud/pom.xml
+++ b/providers/trmk-ecloud/pom.xml
@@ -37,10 +37,10 @@
         <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.api-version>0.8b-ext2.8</test.trmk-ecloud.api-version>
-        <test.trmk-ecloud.build-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 />
+        <test.trmk-ecloud.template></test.trmk-ecloud.template>
 
         <jclouds.osgi.export>org.jclouds.trmk.ecloud*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -116,7 +116,7 @@
                                         <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>
-                                        <test.trmk-ecloud.image-id>${test.trmk-ecloud.image-id}</test.trmk-ecloud.image-id>
+                                        <test.trmk-ecloud.template>${test.trmk-ecloud.template}</test.trmk-ecloud.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/trmk-vcloudexpress/pom.xml b/providers/trmk-vcloudexpress/pom.xml
index e80faed..df454c2 100644
--- a/providers/trmk-vcloudexpress/pom.xml
+++ b/providers/trmk-vcloudexpress/pom.xml
@@ -36,10 +36,10 @@
     <properties>
         <test.trmk-vcloudexpress.endpoint>https://services.vcloudexpress.terremark.com/api</test.trmk-vcloudexpress.endpoint>
         <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.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 />
+        <test.trmk-vcloudexpress.template></test.trmk-vcloudexpress.template>
 
         <jclouds.osgi.export>org.jclouds.trmk.ecloud*;version="${project.version}"</jclouds.osgi.export>
         <jclouds.osgi.import>
@@ -114,7 +114,7 @@
                                         <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>
+                                        <test.trmk-vcloudexpress.template>${test.trmk-vcloudexpress.template}</test.trmk-vcloudexpress.template>
                                     </systemPropertyVariables>
                                 </configuration>
                             </execution>
diff --git a/providers/trystack-nova/pom.xml b/providers/trystack-nova/pom.xml
index 5f85e5c..ba50265 100644
--- a/providers/trystack-nova/pom.xml
+++ b/providers/trystack-nova/pom.xml
@@ -36,13 +36,10 @@
   <properties>
     <test.trystack-nova.endpoint>https://nova-api.trystack.org:5443</test.trystack-nova.endpoint>
     <test.trystack-nova.api-version>1.1</test.trystack-nova.api-version>
-    <test.trystack-nova.build-version />
+    <test.trystack-nova.build-version></test.trystack-nova.build-version>
     <test.trystack-nova.identity>FIXME_IDENTITY</test.trystack-nova.identity>
     <test.trystack-nova.credential>FIXME_CREDENTIAL</test.trystack-nova.credential>
-    <test.trystack-nova.image-id />
-    <test.trystack-nova.image.login-user />
-    <test.trystack-nova.image.authenticate-sudo />
-
+    <test.trystack-nova.template></test.trystack-nova.template>
     <jclouds.osgi.export>org.jclouds.trystack.nova*;version="${project.version}"</jclouds.osgi.export>
     <jclouds.osgi.import>
       org.jclouds.compute.internal;version="${project.version}",
@@ -122,9 +119,7 @@
                     <test.trystack-nova.build-version>${test.trystack-nova.build-version}</test.trystack-nova.build-version>
                     <test.trystack-nova.identity>${test.trystack-nova.identity}</test.trystack-nova.identity>
                     <test.trystack-nova.credential>${test.trystack-nova.credential}</test.trystack-nova.credential>
-                    <test.trystack-nova.image-id>${test.trystack-nova.image-id}</test.trystack-nova.image-id>
-                    <test.trystack-nova.image.login-user>${test.trystack-nova.image.login-user}</test.trystack-nova.image.login-user>
-                    <test.trystack-nova.image.authenticate-sudo>${test.trystack-nova.image.authenticate-sudo}</test.trystack-nova.image.authenticate-sudo>
+                    <test.trystack-nova.template>${test.trystack-nova.template}</test.trystack-nova.template>
                   </systemPropertyVariables>
                 </configuration>
               </execution>
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/EnvBuilder.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/EnvBuilder.java
index ad65abc..6851bb4 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/EnvBuilder.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/EnvBuilder.java
@@ -42,6 +42,8 @@
 
    /**
     * Exports a variable inside the script
+    * 
+    * @param name name of the variable in UPPER_UNDERSCORE case format
     */
    public EnvBuilder export(String name, String value) {
       variables.put(checkNotNull(name, "name"), checkNotNull(value, "value"));
@@ -66,7 +68,7 @@
       builder.append(Utils.writeComment(" Example usage to set a variable", osFamily));
       builder.append(Utils.writeComment("", osFamily));
       builder.append(Utils.writeComment(" "
-               + Utils.writeVariableExporters(ImmutableMap.of("mavenOpts", "-Xms64m -Xmx128m"),
+               + Utils.writeVariableExporters(ImmutableMap.of("MAVEN_OPTS", "-Xms64m -Xmx128m"),
                         osFamily), osFamily));
       builder.append(Utils.writeVariableExporters(variables, osFamily));
       builder.append(ShellToken.LF.to(osFamily));
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java
index 3b0f3e7..c92c078 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java
@@ -67,10 +67,13 @@
       this(instanceName, instanceHome, logDir, variables, ImmutableSet.<Statement> of(), statements);
    }
 
+   /**
+    * @param variables keys are the variables to export in UPPER_UNDERSCORE case format
+    */
    public InitBuilder(String instanceName, String instanceHome, String logDir, Map<String, String> variables,
          Iterable<Statement> initStatements, Iterable<Statement> statements) {
-      Map<String, String> defaultVariables = ImmutableMap.of("instanceName", instanceName, "instanceHome",
-            instanceHome, "logDir", logDir);
+      Map<String, String> defaultVariables = ImmutableMap.of("INSTANCE_NAME", instanceName, "INSTANCE_HOME",
+            instanceHome, "LOG_DIR", logDir);
       this.initStatement = new StatementList(initStatements);
       this.createRunScript = createRunScript(instanceName,// TODO: convert
             // so
@@ -79,9 +82,9 @@
             // can take from a
             // variable
             Iterables.concat(variables.keySet(), defaultVariables.keySet()), "{varl}INSTANCE_HOME{varr}", statements);
-      this.instanceName = checkNotNull(instanceName, "instanceName");
-      this.instanceHome = checkNotNull(instanceHome, "instanceHome");
-      this.logDir = checkNotNull(logDir, "logDir");
+      this.instanceName = checkNotNull(instanceName, "INSTANCE_NAME");
+      this.instanceHome = checkNotNull(instanceHome, "INSTANCE_HOME");
+      this.logDir = checkNotNull(logDir, "LOG_DIR");
 
       addEnvironmentVariableScope("default", defaultVariables)
             .addEnvironmentVariableScope(instanceName, variables)
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitScript.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitScript.java
index 651cc60..38134dc 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitScript.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitScript.java
@@ -70,7 +70,7 @@
        * @see InitScript#getInstanceName()
        */
       public Builder name(String instanceName) {
-         this.instanceName = checkNotNull(instanceName, "instanceName");
+         this.instanceName = checkNotNull(instanceName, "INSTANCE_NAME");
          return this;
       }
 
@@ -78,7 +78,7 @@
        * @see InitScript#getInstanceHome()
        */
       public Builder home(String instanceHome) {
-         this.instanceHome = checkNotNull(instanceHome, "instanceHome");
+         this.instanceHome = checkNotNull(instanceHome, "INSTANCE_HOME");
          return this;
       }
 
@@ -86,11 +86,12 @@
        * @see InitScript#getLogDir()
        */
       public Builder logDir(String logDir) {
-         this.logDir = checkNotNull(logDir, "logDir");
+         this.logDir = checkNotNull(logDir, "LOG_DIR");
          return this;
       }
 
       /**
+       * @param exports keys are the variables to export in UPPER_UNDERSCORE case format
        * @see InitScript#getExportedVariables()
        */
       public Builder exportVariables(Map<String, String> exports) {
@@ -176,11 +177,14 @@
    protected final StatementList run;
    protected final ScriptBuilder delegate;
 
+   /**
+    * @param exports keys are the variables to export in UPPER_UNDERSCORE case format
+    */
    protected InitScript(String instanceName, String instanceHome, String logDir, Map<String, String> exports,
          StatementList init, StatementList run) {
-      this.instanceName = checkNotNull(instanceName, "instanceName");
-      this.instanceHome = checkNotNull(instanceHome, "instanceHome");
-      this.logDir = checkNotNull(logDir, "logDir");
+      this.instanceName = checkNotNull(instanceName, "INSTANCE_NAME");
+      this.instanceHome = checkNotNull(instanceHome, "INSTANCE_HOME");
+      this.logDir = checkNotNull(logDir, "LOG_DIR");
       this.exports = ImmutableMap.<String, String> copyOf(checkNotNull(exports, "exports"));
       this.init = checkNotNull(init, "init");
       this.run = checkNotNull(run, "run");
@@ -188,10 +192,14 @@
       this.delegate = makeInitScriptStatement(instanceName, instanceHome, logDir, exports, init, run);
    }
 
+   /**
+    * 
+    * @param exports keys are the variables to export in UPPER_UNDERSCORE case format
+    */
    public static ScriptBuilder makeInitScriptStatement(String instanceName, String instanceHome, String logDir,
          Map<String, String> exports, StatementList init, StatementList run) {
-      Map<String, String> defaultExports = ImmutableMap.of("instanceName", instanceName, "instanceHome", instanceHome,
-            "logDir", logDir);
+      Map<String, String> defaultExports = ImmutableMap.of("INSTANCE_NAME", instanceName, "INSTANCE_HOME", instanceHome,
+            "LOG_DIR", logDir);
       String exitStatusFile = format("%s/rc", logDir);
       run = new StatementList(ImmutableList.<Statement> builder().add(interpret("rm -f " + exitStatusFile))
             .add(interpret(format("trap 'echo $?>%s' 0 1 2 3 15", exitStatusFile))).addAll(run.delegate()).build());
@@ -303,7 +311,7 @@
 
    @Override
    public String toString() {
-      return toStringHelper(this).add("instanceName", instanceName).toString();
+      return toStringHelper(this).add("INSTANCE_NAME", instanceName).toString();
    }
 
    @Override
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java
index 7c71ce8..5df74dd 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java
@@ -57,7 +57,7 @@
    Map<String, Map<String, String>> variableScopes = Maps.newLinkedHashMap();
 
    @VisibleForTesting
-   List<String> variablesToUnset = Lists.newArrayList("path", "javaHome", "libraryPath");
+   List<String> variablesToUnset = Lists.newArrayList("PATH", "JAVA_HOME", "LIBRARY_PATH");
 
    public ScriptBuilder addStatement(Statement statement) {
       statements.add(checkNotNull(statement, "statement"));
@@ -66,6 +66,7 @@
 
    /**
     * Unsets a variable to ensure it is set within the script.
+    * @param variable name in UPPER_UNDERSCORE case format
     */
    public ScriptBuilder unsetEnvironmentVariable(String name) {
       variablesToUnset.add(checkNotNull(name, "name"));
@@ -74,6 +75,8 @@
 
    /**
     * Exports a variable inside the script
+    * @param scopeName
+    * @param variables keys are the variables to export in UPPER_UNDERSCORE case format
     */
    public ScriptBuilder addEnvironmentVariableScope(String scopeName, Map<String, String> variables) {
       variableScopes.put(checkNotNull(scopeName, "scopeName"), checkNotNull(variables, "variables"));
@@ -112,22 +115,12 @@
       functions.put("abort", Utils.writeFunctionFromResource("abort", osFamily));
 
       for (Entry<String, Map<String, String>> entry : variableScopes.entrySet()) {
-         functions.put(entry.getKey(),
-               Utils.writeFunction(entry.getKey(), Utils.writeVariableExporters(entry.getValue())));
+         functions.put(entry.getKey(), Utils.writeFunction(entry.getKey(), Utils.writeVariableExporters(entry
+                  .getValue(), osFamily)));
       }
-      final Map<String, String> tokenValueMap = ShellToken.tokenValueMap(osFamily);
       StringBuilder builder = new StringBuilder();
       builder.append(ShellToken.BEGIN_SCRIPT.to(osFamily));
-      builder.append(Utils.writeUnsetVariables(
-            Lists.newArrayList(Iterables.transform(variablesToUnset, new Function<String, String>() {
-               @Override
-               public String apply(String from) {
-                  if (tokenValueMap.containsKey(from + "Variable"))
-                     return Utils.FUNCTION_UPPER_UNDERSCORE_TO_LOWER_CAMEL.apply(tokenValueMap.get(from + "Variable"));
-                  return from;
-               }
-
-            })), osFamily));
+      builder.append(Utils.writeUnsetVariables(variablesToUnset, osFamily));
       Map<String, String> functionsToWrite = resolveFunctionDependenciesForStatements(functions, statements, osFamily);
       writeFunctions(functionsToWrite, osFamily, builder);
       builder.append(Utils.writeZeroPath(osFamily));
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AppendFile.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AppendFile.java
index 4f674cf..8fd534c 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AppendFile.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/AppendFile.java
@@ -37,7 +37,7 @@
  * @author Adrian Cole
  */
 public class AppendFile implements Statement {
-   public static final String DELIMETER = "END_OF_JCLOUDS_FILE";
+   public static final String DELIMITER = "END_OF_JCLOUDS_FILE";
 
    public static Builder builder() {
       return new Builder();
@@ -46,7 +46,7 @@
    public static class Builder {
       protected String path;
       protected Iterable<String> lines = ImmutableSet.of();
-      protected String delimeter = DELIMETER;
+      protected String delimiter = DELIMITER;
       protected boolean expandVariables;
 
       /**
@@ -66,10 +66,10 @@
       }
 
       /**
-       * @see AppendFile#getDelimeter()
+       * @see AppendFile#getDelimiter()
        */
-      public Builder delimeter(String delimeter) {
-         this.delimeter = delimeter;
+      public Builder delimiter(String delimiter) {
+         this.delimiter = delimiter;
          return this;
       }
 
@@ -82,19 +82,19 @@
       }
 
       public AppendFile build() {
-         return new AppendFile(path, lines, delimeter, expandVariables);
+         return new AppendFile(path, lines, delimiter, expandVariables);
       }
    }
 
    protected final String path;
    protected final Iterable<String> lines;
-   protected final String delimeter;
+   protected final String delimiter;
    protected final boolean expandVariables;
 
-   protected AppendFile(String path, Iterable<String> lines, String delimeter, boolean expandVariables) {
-      this.path = checkNotNull(path, "path");
+   protected AppendFile(String path, Iterable<String> lines, String delimiter, boolean expandVariables) {
+      this.path = checkNotNull(path, "PATH");
       this.lines = checkNotNull(lines, "lines");
-      this.delimeter = checkNotNull(delimeter, "delimeter");
+      this.delimiter = checkNotNull(delimiter, "delimiter");
       checkState(Iterables.size(lines) > 0, "you must pass something to execute");
       this.expandVariables = expandVariables;
    }
@@ -140,15 +140,15 @@
       for (String line : lines) {
          hereFile.append('\t').append(line).append("\n");
       }
-      hereFile.append(delimeter).append("\n");
+      hereFile.append(delimiter).append("\n");
       return hereFile.toString();
    }
 
    public StringBuilder startHereFile() {
       StringBuilder hereFile = new StringBuilder().append("cat >> ").append(path);
       if (expandVariables)
-         return hereFile.append(" <<-").append(delimeter).append("\n");
-      return hereFile.append(" <<-'").append(delimeter).append("'\n");
+         return hereFile.append(" <<-").append(delimiter).append("\n");
+      return hereFile.append(" <<-'").append(delimiter).append("'\n");
    }
 
    protected String appendLineToWindowsFile(String line, String path) {
@@ -161,4 +161,4 @@
       return String.format("echo %s%s%s >>%s{lf}", quote, line, quote, path);
    }
 
-}
\ No newline at end of file
+}
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateOrOverwriteFile.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateOrOverwriteFile.java
index 04bb3ab..f0eb424 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateOrOverwriteFile.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateOrOverwriteFile.java
@@ -42,8 +42,8 @@
       }
 
       @Override
-      public Builder delimeter(String delimeter) {
-         return Builder.class.cast(super.delimeter(delimeter));
+      public Builder delimiter(String delimiter) {
+         return Builder.class.cast(super.delimiter(delimiter));
       }
 
       @Override
@@ -53,13 +53,13 @@
 
       @Override
       public CreateOrOverwriteFile build() {
-         return new CreateOrOverwriteFile(path, lines, delimeter, expandVariables);
+         return new CreateOrOverwriteFile(path, lines, delimiter, expandVariables);
       }
 
    }
 
-   protected CreateOrOverwriteFile(String path, Iterable<String> lines, String delimeter, boolean expandVariables) {
-      super(path, lines, delimeter, expandVariables);
+   protected CreateOrOverwriteFile(String path, Iterable<String> lines, String delimiter, boolean expandVariables) {
+      super(path, lines, delimiter, expandVariables);
    }
 
    @Override
@@ -71,8 +71,8 @@
    public StringBuilder startHereFile() {
       StringBuilder hereFile = new StringBuilder().append("cat > ").append(path);
       if (expandVariables)
-         return hereFile.append(" <<-").append(delimeter).append("\n");
-      return hereFile.append(" <<-'").append(delimeter).append("'\n");
+         return hereFile.append(" <<-").append(delimiter).append("\n");
+      return hereFile.append(" <<-'").append(delimiter).append("'\n");
    }
 
-}
\ No newline at end of file
+}
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java
index 0678107..50ef817 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java
@@ -36,13 +36,12 @@
 import org.jclouds.scriptbuilder.ScriptBuilder;
 import org.jclouds.scriptbuilder.util.Utils;
 
-import com.google.common.base.CaseFormat;
 import com.google.common.base.Function;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.ImmutableList.Builder;
 
 /**
  * Creates a run script
@@ -50,14 +49,18 @@
  * @author Adrian Cole
  */
 public class CreateRunScript extends StatementList {
-   public final static String DELIMETER = "END_OF_JCLOUDS_SCRIPT";
+   public final static String DELIMITER = "END_OF_JCLOUDS_SCRIPT";
    final String instanceName;
    final Iterable<String> exports;
    final String pwd;
-
+   
+   /**
+    * @param exports
+    *            variable names to export in UPPER_UNDERSCORE case format
+    */
    public CreateRunScript(String instanceName, Iterable<String> exports, String pwd, Iterable<Statement> statements) {
       super(statements);
-      this.instanceName = checkNotNull(instanceName, "instanceName");
+      this.instanceName = checkNotNull(instanceName, "INSTANCE_NAME");
       this.exports = checkNotNull(exports, "exports");
       this.pwd = checkNotNull(pwd, "pwd").replaceAll("[/\\\\]", "{fs}");
    }
@@ -65,9 +68,13 @@
    public static class AddExport implements Statement {
       final String export;
       final String value;
-
+      
+      /**
+       * @param export
+       *            variable name in UPPER_UNDERSCORE case format
+       */
       public AddExport(String export, String value) {
-         this.export = checkNotNull(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export), "export");
+         this.export = checkNotNull(export, "export");
          this.value = checkNotNull(value, "value");
       }
 
@@ -116,7 +123,7 @@
       builder.append("# add runscript footer\n");
       Iterable<String> endScript = Splitter.on(ShellToken.LF.to(OsFamily.UNIX)).split(
             ShellToken.END_SCRIPT.to(OsFamily.UNIX));
-      builder.append(appendFile(runScript, endScript, DELIMETER).render(OsFamily.UNIX));
+      builder.append(appendFile(runScript, endScript, DELIMITER).render(OsFamily.UNIX));
    }
 
    private void addUnixRunScript(String runScript, StringBuilder builder) {
@@ -131,7 +138,7 @@
          }
          userCommands.addAll(Splitter.on('\n').split(statement.render(OsFamily.UNIX)));
       }
-      builder.append(appendFile(runScript, userCommands.build(), DELIMETER).render(OsFamily.UNIX));
+      builder.append(appendFile(runScript, userCommands.build(), DELIMITER).render(OsFamily.UNIX));
    }
 
    private void addUnixRunScriptHeader(String runScript, StringBuilder builder) {
@@ -143,17 +150,16 @@
       beginningOfFile.add(format("PROMPT_COMMAND='echo -ne \\\"\\033]0;%s\\007\\\"'", instanceName));
       beginningOfFile.add(Utils.writeZeroPath(OsFamily.UNIX));
       beginningOfFile.add(format("export INSTANCE_NAME='%s'", instanceName));
-      builder.append(createOrOverwriteFile(runScript, beginningOfFile.build(), DELIMETER).render(OsFamily.UNIX));
+      builder.append(createOrOverwriteFile(runScript, beginningOfFile.build(), DELIMITER).render(OsFamily.UNIX));
 
       // expanding variables here.
-      builder.append(AppendFile.builder().path(runScript).delimeter(DELIMETER).expandVariables(true)
+      builder.append(AppendFile.builder().path(runScript).delimiter(DELIMITER).expandVariables(true)
             .lines(Iterables.transform(exports, new Function<String, String>() {
 
                @Override
                public String apply(String export) {
-                  String variableNameInUpper = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export);
-                  return new StringBuilder().append("export ").append(variableNameInUpper).append("='$")
-                        .append(variableNameInUpper).append("'").toString();
+                  return new StringBuilder().append("export ").append(export).append("='$")
+                        .append(export).append("'").toString();
                }
             })).build().render(OsFamily.UNIX));
 
@@ -165,7 +171,7 @@
       if (functionsToWrite.size() > 1) {
          StringBuilder functions = new StringBuilder();
          ScriptBuilder.writeFunctions(functionsToWrite, OsFamily.UNIX, functions);
-         builder.append(appendFile(runScript, functions.toString(), DELIMETER).render(OsFamily.UNIX));
+         builder.append(appendFile(runScript, functions.toString(), DELIMITER).render(OsFamily.UNIX));
       }
    }
-}
\ No newline at end of file
+}
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InitMetadata.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InitMetadata.java
index 7c0f265..b933d10 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InitMetadata.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InitMetadata.java
@@ -49,7 +49,7 @@
       this.stopDir = checkNotNull(stopDir, "stopDir");
       this.configDir = checkNotNull(configDir, "configDir");
       this.dataDir = checkNotNull(dataDir, "dataDir");
-      this.logDir = checkNotNull(logDir, "logDir");
+      this.logDir = checkNotNull(logDir, "LOG_DIR");
       this.goldDir = checkNotNull(goldDir, "goldDir");
    }
 
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/JavaInitMetadata.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/JavaInitMetadata.java
index 999147f..765245c 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/JavaInitMetadata.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/JavaInitMetadata.java
@@ -40,7 +40,7 @@
             String stopDir, String configDir, String dataDir, String logDir, String goldDir,
             String javaHome, String[] classpath, String mainClass, String[] opts, String[] args) {
       super(name, platformHome, endPoint, startDir, stopDir, configDir, dataDir, logDir, goldDir);
-      this.javaHome = checkNotNull(javaHome, "javaHome");
+      this.javaHome = checkNotNull(javaHome, "JAVA_HOME");
       this.classpath = checkNotNull(classpath, "classpath");
       this.mainClass = checkNotNull(mainClass, "mainClass");
       this.opts = checkNotNull(opts, "opts");
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java
index 76c6bf6..bfc6df4 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java
@@ -65,26 +65,30 @@
       return new Call(function, args);
    }
 
-   public static Statement appendFile(String path, String line, String delimeter) {
-      return AppendFile.builder().path(path).lines(ImmutableSet.of(line)).delimeter(delimeter).build();
+   public static Statement appendFile(String path, String line, String delimiter) {
+      return AppendFile.builder().path(path).lines(ImmutableSet.of(line)).delimiter(delimiter).build();
    }
 
    public static Statement appendFile(String path, Iterable<String> lines) {
       return AppendFile.builder().path(path).lines(lines).build();
    }
 
-   public static Statement appendFile(String path, Iterable<String> lines, String delimeter) {
-      return AppendFile.builder().path(path).lines(lines).delimeter(delimeter).build();
+   public static Statement appendFile(String path, Iterable<String> lines, String delimiter) {
+      return AppendFile.builder().path(path).lines(lines).delimiter(delimiter).build();
    }
 
    public static Statement createOrOverwriteFile(String path, Iterable<String> lines) {
       return CreateOrOverwriteFile.builder().path(path).lines(lines).build();
    }
 
-   public static Statement createOrOverwriteFile(String path, Iterable<String> lines, String delimeter) {
-      return CreateOrOverwriteFile.builder().path(path).lines(lines).delimeter(delimeter).build();
+   public static Statement createOrOverwriteFile(String path, Iterable<String> lines, String delimiter) {
+      return CreateOrOverwriteFile.builder().path(path).lines(lines).delimiter(delimiter).build();
    }
 
+   /**
+    * @param exports
+    *            variable names to export in UPPER_UNDERSCORE case format
+    */
    public static CreateRunScript createRunScript(String instanceName, Iterable<String> exports, String pwd,
          Iterable<Statement> statements) {// TODO: convert so
       // that
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SwitchArg.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SwitchArg.java
index 289ecc7..17841ba 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SwitchArg.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SwitchArg.java
@@ -94,11 +94,11 @@
             if (shouldIndent)
                actionBuilder.append(INDENT);
             actionBuilder.append(line).append(ShellToken.LF.to(family));
-            if (line.indexOf(CreateRunScript.DELIMETER) != -1) {
+            if (line.indexOf(CreateRunScript.DELIMITER) != -1) {
                inRunScript = inRunScript ? false : true;
 
             }
-            if (line.indexOf(AppendFile.DELIMETER) != -1) {
+            if (line.indexOf(AppendFile.DELIMITER) != -1) {
                inCreateFile = inCreateFile ? false : true;
             }
             shouldIndent = !inCreateFile && !inRunScript;
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/FunctionNotFoundException.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/FunctionNotFoundException.java
index f6ba30f..9c58412 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/FunctionNotFoundException.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/FunctionNotFoundException.java
@@ -27,10 +27,10 @@
   private static final long serialVersionUID = 1L;
 
   public FunctionNotFoundException(String functionName, OsFamily family) {
-    super("function: " + functionName + " not found for famiy: " + family);
+    super("function: " + functionName + " not found for family: " + family);
   }
 
   public FunctionNotFoundException(String functionName, OsFamily family, Throwable cause) {
-    super("function: " + functionName + " not found for famiy: " + family, cause);
+    super("function: " + functionName + " not found for family: " + family, cause);
   }
 }
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java
index e22e5a6..b8f2f8d 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.scriptbuilder.util;
 
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.regex.Matcher;
@@ -27,6 +26,7 @@
 import org.jclouds.scriptbuilder.domain.OsFamily;
 import org.jclouds.scriptbuilder.domain.ShellToken;
 import org.jclouds.scriptbuilder.functionloader.CurrentFunctionLoader;
+import org.jclouds.util.Maps2;
 
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Function;
@@ -41,28 +41,35 @@
  */
 public class Utils {
 
-   public static final LowerCamelToUpperUnderscore FUNCTION_LOWER_CAMEL_TO_UPPER_UNDERSCORE = new LowerCamelToUpperUnderscore();
+   /**
+    * 
+    * In {@link ShellToken}, the values whose names end in {@code _VARIABLE} designate variable
+    * names we know how to translate from one platform to another. For example
+    * {@link ShellToken#LIBRARY_PATH_VARIABLE} means that we can translate the variable named
+    * {@code LIBRARY_PATH} to the proper platform-specific name.
+    */
+   public static final class VariableNameForOsFamily implements Function<String, String> {
+      private final OsFamily family;
 
-   public static final class LowerCamelToUpperUnderscore implements Function<String, String> {
+      public VariableNameForOsFamily(OsFamily family) {
+         this.family = family;
+      }
+
       @Override
-      public String apply(String from) {
-         return CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, from);
+      public String apply(String input) {
+         String variableNameKey = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input) + "Variable";
+         if (ShellToken.tokenValueMap(family).containsKey(variableNameKey))
+            return ShellToken.tokenValueMap(family).get(variableNameKey);
+         return input;
       }
    }
 
-   public static final UpperUnderscoreToLowerCamel FUNCTION_UPPER_UNDERSCORE_TO_LOWER_CAMEL = new UpperUnderscoreToLowerCamel();
-
-   public static final class UpperUnderscoreToLowerCamel implements Function<String, String> {
-      @Override
-      public String apply(String from) {
-         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, from);
-      }
-   }
-
-   /** matches any expression inside curly braces (where the expression does not including an open curly brace) */
+   /**
+    * matches any expression inside curly braces (where the expression does not including an open
+    * curly brace)
+    */
    private static final Pattern pattern = Pattern.compile("\\{([^\\{]+?)\\}");
 
-
    /**
     * replaces tokens that are expressed as <code>{token}</code>
     * 
@@ -96,32 +103,31 @@
    /**
     * converts a map into variable exports relevant to the specified platform.
     * <p/>
-    * ex. if variablesInLowerCamelCase is "mavenOpts" -> "-Xms64m -Xmx256m" <br/>
+    * ex. if {@code keys} is the map: "MAVEN_OPTS" -> "-Xms64m -Xmx256m" <br/>
     * and family is UNIX<br/>
     * then writeVariableExporters returns literally {@code export MAVEN_OPTS="-Xms64m -Xmx256m"\n}
     * 
-    * @param variablesInLowerCamelCase
-    *           lower camel keys to values
+    * @param exports
+    *           keys are the variables to export in UPPER_UNDERSCORE case format
     * @param family
     *           operating system for formatting
+    * @see VariableNameForOsFamily
     */
-   public static String writeVariableExporters(Map<String, String> variablesInLowerCamelCase,
-            OsFamily family) {
-      return replaceTokens(writeVariableExporters(variablesInLowerCamelCase), ShellToken
-               .tokenValueMap(family));
+   public static String writeVariableExporters(Map<String, String> exports, final OsFamily family) {
+      exports = Maps2.transformKeys(exports, new VariableNameForOsFamily(family));
+      return replaceTokens(writeVariableExporters(exports), ShellToken.tokenValueMap(family));
    }
 
    /**
     * converts a map into variable exporters in shell intermediate language.
     * 
-    * @param variablesInLowerCamelCase
-    *           lower camel keys to values
+    * @param exports
+    *           keys are the variables to export in UPPER_UNDERSCORE case format
     */
-   public static String writeVariableExporters(Map<String, String> variablesInLowerCamelCase) {
+   public static String writeVariableExporters(Map<String, String> exports) {
       StringBuilder initializers = new StringBuilder();
-      for (Entry<String, String> entry : variablesInLowerCamelCase.entrySet()) {
-         String key = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, entry.getKey());
-         initializers.append(String.format("{export} %s={vq}%s{vq}{lf}", key, entry.getValue()));
+      for (Entry<String, String> entry : exports.entrySet()) {
+         initializers.append(String.format("{export} %s={vq}%s{vq}{lf}", entry.getKey(), entry.getValue()));
       }
       return initializers.toString();
    }
@@ -131,36 +137,34 @@
    }
 
    public static String writeFunctionFromResource(String function, OsFamily family) {
-         String toReturn = CurrentFunctionLoader.get().loadFunction(function,family);
-         String lf = ShellToken.LF.to(family);
-         return toReturn.endsWith(lf) ? toReturn : new StringBuilder(toReturn).append(lf).toString();
+      String toReturn = CurrentFunctionLoader.get().loadFunction(function, family);
+      String lf = ShellToken.LF.to(family);
+      return toReturn.endsWith(lf) ? toReturn : new StringBuilder(toReturn).append(lf).toString();
    }
 
-  public static String writeFunction(String function, String source) {
+   public static String writeFunction(String function, String source) {
       return String.format("{fncl}%s{fncr}%s{fnce}", function, source.replaceAll("^", "   "));
    }
 
-   public static final Map<OsFamily, String> OS_TO_POSITIONAL_VAR_PATTERN = ImmutableMap.of(
-            OsFamily.UNIX, "set {key}=$1\nshift\n", OsFamily.WINDOWS, "set {key}=%1\r\nshift\r\n");
+   public static final Map<OsFamily, String> OS_TO_POSITIONAL_VAR_PATTERN = ImmutableMap.of(OsFamily.UNIX,
+            "set {key}=$1\nshift\n", OsFamily.WINDOWS, "set {key}=%1\r\nshift\r\n");
 
-   public static final Map<OsFamily, String> OS_TO_LOCAL_VAR_PATTERN = ImmutableMap.of(
-            OsFamily.UNIX, "set {key}=\"{value}\"\n", OsFamily.WINDOWS, "set {key}={value}\r\n");
+   public static final Map<OsFamily, String> OS_TO_LOCAL_VAR_PATTERN = ImmutableMap.of(OsFamily.UNIX,
+            "set {key}=\"{value}\"\n", OsFamily.WINDOWS, "set {key}={value}\r\n");
 
    /**
     * Writes an initialization statement for use inside a script or a function.
     * 
-    * @param positionalVariablesInLowerCamelCase
+    * @param positionalVariables
     *           - transfer the value of args into these statements. Note that there is no check to
     *           ensure that all source args are indeed present.
+    * 
     */
-   public static String writePositionalVars(List<String> positionalVariablesInLowerCamelCase,
-            OsFamily family) {
+   public static String writePositionalVars(Iterable<String> positionalVariables, OsFamily family) {
       StringBuilder initializers = new StringBuilder();
-      for (String variableInLowerCamelCase : positionalVariablesInLowerCamelCase) {
-         String key = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE,
-                  variableInLowerCamelCase);
-         initializers.append(replaceTokens(OS_TO_POSITIONAL_VAR_PATTERN.get(family), ImmutableMap
-                  .of("key", key)));
+      for (String positionalVariable : positionalVariables) {
+         initializers.append(replaceTokens(OS_TO_POSITIONAL_VAR_PATTERN.get(family), ImmutableMap.of("key",
+                  positionalVariable)));
       }
       return initializers.toString();
    }
@@ -168,22 +172,20 @@
    /**
     * Ensures that variables come from a known source instead of bleeding in from a profile
     * 
-    * @param variablesInLowerCamelCase
+    * @param variablesToUnset
     *           - System variables to unset
+    * @see VariableNameForOsFamily
     */
-   public static String writeUnsetVariables(List<String> variablesInLowerCamelCase, OsFamily family) {
+   public static String writeUnsetVariables(Iterable<String> variablesToUnset, OsFamily family) {
+      variablesToUnset = Iterables.transform(variablesToUnset, new VariableNameForOsFamily(family));
       switch (family) {
          case UNIX:
-            return String.format("unset %s\n", Joiner.on(' ').join(
-                     Iterables.transform(variablesInLowerCamelCase,
-                              FUNCTION_LOWER_CAMEL_TO_UPPER_UNDERSCORE)));
+            return String.format("unset %s\n", Joiner.on(' ').join(variablesToUnset));
          case WINDOWS:
             StringBuilder initializers = new StringBuilder();
-            for (String variableInLowerCamelCase : variablesInLowerCamelCase) {
-               String key = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE,
-                        variableInLowerCamelCase);
-               initializers.append(replaceTokens(OS_TO_LOCAL_VAR_PATTERN.get(family), ImmutableMap
-                        .of("key", key, "value", "")));
+            for (String variableToUnset : variablesToUnset) {
+               initializers.append(replaceTokens(OS_TO_LOCAL_VAR_PATTERN.get(family), ImmutableMap.of("key",
+                        variableToUnset, "value", "")));
             }
             return initializers.toString();
          default:
@@ -193,8 +195,8 @@
    }
 
    public static final Map<OsFamily, String> OS_TO_ZERO_PATH = ImmutableMap.of(OsFamily.WINDOWS,
-            "set PATH=c:\\windows\\;C:\\windows\\system32;c:\\windows\\system32\\wbem\r\n",
-            OsFamily.UNIX, "export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin\n");
+            "set PATH=c:\\windows\\;C:\\windows\\system32;c:\\windows\\system32\\wbem\r\n", OsFamily.UNIX,
+            "export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin\n");
 
    /**
     * @return line used to zero out the path of the script such that basic commands such as unix ps
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/EnvBuilderTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/EnvBuilderTest.java
index 9b3b9bb..b574989 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/EnvBuilderTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/EnvBuilderTest.java
@@ -39,7 +39,7 @@
  */
 public class EnvBuilderTest {
 
-   EnvBuilder testScriptBuilder = new EnvBuilder().export("javaHome",
+   EnvBuilder testScriptBuilder = new EnvBuilder().export("JAVA_HOME",
             "/apps/jdk1.6");
 
    @Test
@@ -59,8 +59,8 @@
    @Test
    public void testExport() {
       EnvBuilder builder = new EnvBuilder();
-      builder.export("javaHome", "/apps/jdk1.6");
-      assertEquals(builder.variables, ImmutableMap.of("javaHome", "/apps/jdk1.6"));
+      builder.export("JAVA_HOME", "/apps/jdk1.6");
+      assertEquals(builder.variables, ImmutableMap.of("JAVA_HOME", "/apps/jdk1.6"));
    }
 
    @Test
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java
index 60a2000..d2274eb 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java
@@ -45,7 +45,7 @@
 @Deprecated
 public class InitBuilderTest {
 
-   InitBuilder testInitBuilder = new InitBuilder("mkebsboot", "/mnt/tmp", "/mnt/tmp", ImmutableMap.of("tmpDir",
+   InitBuilder testInitBuilder = new InitBuilder("mkebsboot", "/mnt/tmp", "/mnt/tmp", ImmutableMap.of("TMP_DIR",
          "/mnt/tmp"), ImmutableList.<Statement> of(
          appendFile("{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", ImmutableList.<String> of("hello world")),
          exec("find /")));
@@ -69,7 +69,7 @@
             new InitBuilder("mkebsboot",// name of the script
                   "/tmp",// working directory
                   "/tmp/logs",// location of stdout.log and stderr.log
-                  ImmutableMap.of("imageDir", "/mnt/tmp", "ebsDevice", "/dev/sdh", "ebsMountPoint", "/mnt/ebs"),// variables
+                  ImmutableMap.of("IMAGE_DIR", "/mnt/tmp", "EBS_DEVICE", "/dev/sdh", "EBS_MOUNT_POINT", "/mnt/ebs"),// variables
                                                                                                                 // used
                                                                                                                 // inside
                                                                                                                 // of
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java
index 2710927..aa7409a 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java
@@ -49,8 +49,8 @@
 public class ScriptBuilderTest {
 
    ScriptBuilder testScriptBuilder = new ScriptBuilder()
-         .unsetEnvironmentVariable("runtime")
-         .addEnvironmentVariableScope("default", ImmutableMap.of("runtime", "Moo"))
+         .unsetEnvironmentVariable("RUNTIME")
+         .addEnvironmentVariableScope("default", ImmutableMap.of("RUNTIME", "Moo"))
          .addStatement(
                switchArg(1, ImmutableMap.of(
                      "start",
@@ -132,8 +132,8 @@
    @Test
    public void testExport() {
       ScriptBuilder builder = new ScriptBuilder();
-      builder.addEnvironmentVariableScope("default", ImmutableMap.of("javaHome", "/apps/jdk1.6"));
-      assertEquals(builder.variableScopes, ImmutableMap.of("default", ImmutableMap.of("javaHome", "/apps/jdk1.6")));
+      builder.addEnvironmentVariableScope("default", ImmutableMap.of("JAVA_HOME", "/apps/jdk1.6"));
+      assertEquals(builder.variableScopes, ImmutableMap.of("default", ImmutableMap.of("JAVA_HOME", "/apps/jdk1.6")));
    }
 
    @Test
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java
index 46050e8..c265bc1 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java
@@ -39,7 +39,7 @@
 public class CreateRunScriptTest {
    Statement statement = createRunScript(
             "yahooprod",
-            ImmutableList.<String> of("javaHome"),
+            ImmutableList.<String> of("JAVA_HOME"),
             "{tmp}{fs}{uid}{fs}scripttest",
             ImmutableList
                      .<Statement> of(
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/UtilsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/UtilsTest.java
index a1a53ac..022bcd7 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/UtilsTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/UtilsTest.java
@@ -48,36 +48,57 @@
    }
    
    public void testWriteVariableExportersUNIX() {
-      assertEquals(Utils.writeVariableExporters(ImmutableMap.of("mavenOpts",
+      assertEquals(Utils.writeVariableExporters(ImmutableMap.of("MAVEN_OPTS",
                "-Xms128m -Xmx256m -XX:+HeapDumpOnOutOfMemoryError"), OsFamily.UNIX),
                "export MAVEN_OPTS=\"-Xms128m -Xmx256m -XX:+HeapDumpOnOutOfMemoryError\"\n");
    }
 
    public void testWriteVariableExportersWindows() {
-      assertEquals(Utils.writeVariableExporters(ImmutableMap.of("mavenOpts",
+      assertEquals(Utils.writeVariableExporters(ImmutableMap.of("MAVEN_OPTS",
                "-Xms128m -Xmx256m -XX:+HeapDumpOnOutOfMemoryError"), OsFamily.WINDOWS),
                "set MAVEN_OPTS=-Xms128m -Xmx256m -XX:+HeapDumpOnOutOfMemoryError\r\n");
    }
+   
+   public void testWriteVariableExportersNameReplaceUNIX() {
+      assertEquals(Utils.writeVariableExporters(ImmutableMap.of("LIBRARY_PATH", "{tmp}"), OsFamily.UNIX),
+               "export LD_LIBRARY_PATH=\"/tmp\"\n");
+   }
+
+   public void testWriteVariableExportersNameReplaceWindows() {
+      assertEquals(Utils.writeVariableExporters(ImmutableMap.of("LIBRARY_PATH", "{tmp}"), OsFamily.WINDOWS),
+               "set PATH=%TEMP%\r\n");
+   }
 
    public void testWritePositionalVarsUNIX() {
-      assertEquals(Utils.writePositionalVars(ImmutableList.of("host", "port"), OsFamily.UNIX),
+      assertEquals(Utils.writePositionalVars(ImmutableList.of("HOST", "PORT"), OsFamily.UNIX),
                "set HOST=$1\nshift\nset PORT=$1\nshift\n");
    }
 
    public void testWritePositionalVarsWindows() {
-      assertEquals(Utils.writePositionalVars(ImmutableList.of("host", "port"), OsFamily.WINDOWS),
+      assertEquals(Utils.writePositionalVars(ImmutableList.of("HOST", "PORT"), OsFamily.WINDOWS),
                "set HOST=%1\r\nshift\r\nset PORT=%1\r\nshift\r\n");
    }
 
    public void testWriteUnsetVariablesUNIX() {
-      assertEquals(Utils.writeUnsetVariables(ImmutableList.of("host", "port"), OsFamily.UNIX),
+      assertEquals(Utils.writeUnsetVariables(ImmutableList.of("HOST", "PORT"), OsFamily.UNIX),
                "unset HOST PORT\n");
    }
 
    public void testWriteUnsetVariablesWindows() {
-      assertEquals(Utils.writeUnsetVariables(ImmutableList.of("host", "port"), OsFamily.WINDOWS),
+      assertEquals(Utils.writeUnsetVariables(ImmutableList.of("HOST", "PORT"), OsFamily.WINDOWS),
                "set HOST=\r\nset PORT=\r\n");
    }
+   
+
+   public void testWriteUnsetVariablesNameReplaceUNIX() {
+      assertEquals(Utils.writeUnsetVariables(ImmutableList.of("LIBRARY_PATH"), OsFamily.UNIX),
+               "unset LD_LIBRARY_PATH\n");
+   }
+
+   public void testWriteUnsetVariablesNameReplaceWindows() {
+      assertEquals(Utils.writeUnsetVariables(ImmutableList.of("LIBRARY_PATH"), OsFamily.WINDOWS),
+               "set PATH=\r\n");
+   }
 
    public void testSingleCurlyBraceDoesntBreakLfTokenReplacement() {
       assertEquals(Utils.replaceTokens("{{lf}", ShellToken.tokenValueMap(OsFamily.UNIX)),