Merge branch 'master' of github.com:jclouds/jclouds into 1.5.x

* 'master' of github.com:jclouds/jclouds: (34 commits)
  Issue 899:NPE on cleanUpIncidentalResourcesOfDeadNodes
  openstack-nova-ec2 errors on authorizing security group to itself, and also needs to auto-allocate elastic ips
  updated error parser to work with Nova EC2 error message
  moved cacheloaders to the correct package
  default to allocate elastic ips on openstack-nova-ec
  more details when image doesn't match
  PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest
  fix Accept header in delete
  fix (unused) createImage http body in 404 test.
  add tests for createImage, switch argument order.
  remove unused import statements.
  create an image from a nova vm instance.
  Issue 891: update to support lucid
  added openstack-nova-ec2 to allcompute
  Updated PlatformServices attribute name for RUN@cloud's TweetStore version
  Updated PlatformServices attribute name for OpenShift's TweetStore version
  ensure tests are tidy, consistent, and clean up after themselves
  refactor out test session to exist on the suite scope
  make adminContext more robust
  refactor out admin tests
  ...
diff --git a/README.txt b/README.txt
index 2f8c07f..d8ec695 100644
--- a/README.txt
+++ b/README.txt
@@ -23,7 +23,7 @@
                           openhosting-east1, serverlove-z1-man, skalicloud-sdg-my,
                           greenhousedata-element-vcloud, softlayer, cloudsigma (generic),
                           cloudstack (generic), ninefold-compute, openstack-nov (keystone),
-                          hpcloud-compute, trystack-nova
+                          hpcloud-compute, trystack-nova, openstack-nova-ec2
 
   * note * the pom dependency org.jclouds/jclouds-allcompute gives you access to
            to all of these providers
diff --git a/allcompute/pom.xml b/allcompute/pom.xml
index 750bae6..907098c 100644
--- a/allcompute/pom.xml
+++ b/allcompute/pom.xml
@@ -62,6 +62,11 @@
         </dependency>
         <dependency>
             <groupId>org.jclouds.api</groupId>
+            <artifactId>openstack-nova-ec2</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds.api</groupId>
             <artifactId>nova</artifactId>
             <version>${project.version}</version>
         </dependency>
diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/AtmosApiMetadata.java b/apis/atmos/src/main/java/org/jclouds/atmos/AtmosApiMetadata.java
new file mode 100644
index 0000000..20fe7cd
--- /dev/null
+++ b/apis/atmos/src/main/java/org/jclouds/atmos/AtmosApiMetadata.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.atmos;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for EMC's Atmos API.
+ * 
+ * @author Adrian Cole
+ */
+public class AtmosApiMetadata extends BaseApiMetadata {
+
+   public AtmosApiMetadata() {
+      this(builder()
+            .id("atmos")
+            .type(ApiType.BLOBSTORE)
+            .name("EMC's Atmos API")
+            .identityName("Subtenant ID (UID)")
+            .credentialName("Shared Secret")
+            .documentation(URI.create("https://community.emc.com/docs/DOC-10508")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AtmosApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public AtmosApiMetadata build() {
+         return new AtmosApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/atmos/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/atmos/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..3cc1141
--- /dev/null
+++ b/apis/atmos/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.atmos.AtmosApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/AtmosApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/atmos/src/test/java/org/jclouds/atmos/AtmosApiMetadataTest.java
index ace0f456..9f5f407 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/atmos/src/test/java/org/jclouds/atmos/AtmosApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.atmos;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "AtmosApiMetadataTest")
+public class AtmosApiMetadataTest extends BaseApiMetadataTest {
 
+   public AtmosApiMetadataTest() {
+      super(new AtmosApiMetadata(), ApiType.BLOBSTORE);
+   }
 }
diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/ProvidersInPropertiesTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/ProvidersInPropertiesTest.java
deleted file mode 100644
index 6f21731..0000000
--- a/apis/atmos/src/test/java/org/jclouds/atmos/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,47 +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.atmos;
-
-import org.jclouds.blobstore.util.BlobStoreUtils;
-import org.jclouds.rest.Providers;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "atmos") : providers;
-   }
-
-   @Test
-   public void testSupportedBlobStoreProviders() {
-      Iterable<String> providers = BlobStoreUtils.getSupportedProviders();
-      assert Iterables.contains(providers, "atmos") : providers;
-   }
-
-}
diff --git a/apis/byon/src/main/java/org/jclouds/byon/BYONApiMetadata.java b/apis/byon/src/main/java/org/jclouds/byon/BYONApiMetadata.java
new file mode 100644
index 0000000..b818a6e
--- /dev/null
+++ b/apis/byon/src/main/java/org/jclouds/byon/BYONApiMetadata.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.byon;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for jclouds BYON API
+ * 
+ * @author Adrian Cole
+ */
+public class BYONApiMetadata extends BaseApiMetadata {
+
+   public BYONApiMetadata() {
+      this(builder()
+            .id("byon")
+            .type(ApiType.COMPUTE)
+            .name("Bring Your Own Node (BYON) API")
+            .identityName("Unused")
+            .documentation(URI.create("https://github.com/jclouds/jclouds/tree/master/apis/byon")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected BYONApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public BYONApiMetadata build() {
+         return new BYONApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/byon/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/byon/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..2816908
--- /dev/null
+++ b/apis/byon/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.byon.BYONApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/byon/src/test/java/org/jclouds/byon/BYONApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/byon/src/test/java/org/jclouds/byon/BYONApiMetadataTest.java
index ace0f456..6265ff3 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/byon/src/test/java/org/jclouds/byon/BYONApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.byon;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "BYONApiMetadataTest")
+public class BYONApiMetadataTest extends BaseApiMetadataTest {
 
+   public BYONApiMetadataTest() {
+      super(new BYONApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/CloudFilesApiMetadata.java b/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/CloudFilesApiMetadata.java
new file mode 100644
index 0000000..654c040
--- /dev/null
+++ b/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/CloudFilesApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.cloudfiles;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Rackspace Cloud Files API
+ * 
+ * @author Dan Lo Bianco
+ */
+public class CloudFilesApiMetadata extends BaseApiMetadata {
+
+   public CloudFilesApiMetadata() {
+      this(builder()
+            .id("cloudfiles")
+            .type(ApiType.BLOBSTORE)
+            .name("Rackspace Cloud Files API")
+            .identityName("Username")
+            .credentialName("API Key")
+            .documentation(URI.create("http://docs.rackspacecloud.com/files/api/v1/cfdevguide_d5/content/ch01.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudFilesApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudFilesApiMetadata build() {
+         return new CloudFilesApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/CloudFilesProviderMetadata.java b/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/CloudFilesProviderMetadata.java
deleted file mode 100644
index 6fcc489..0000000
--- a/apis/cloudfiles/src/main/java/org/jclouds/cloudfiles/CloudFilesProviderMetadata.java
+++ /dev/null
@@ -1,69 +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.cloudfiles;
-
-import java.net.URI;
-
-import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-/**
- * Common implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud Files
- * 
- * @author Dan Lo Bianco
- */
-public abstract class CloudFilesProviderMetadata extends BaseProviderMetadata {
-
-	public CloudFilesProviderMetadata() {
-		super();
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getType() {
-		return ProviderMetadata.BLOBSTORE_TYPE;
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getIdentityName() {
-		return "Username";
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getCredentialName() {
-		return "API Key";
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public URI getApiDocumentation() {
-		return URI.create("http://docs.rackspacecloud.com/files/api/v1/cfdevguide_d5/content/ch01.html");
-	}
-
-}
\ No newline at end of file
diff --git a/apis/cloudfiles/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/cloudfiles/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..4ae7611
--- /dev/null
+++ b/apis/cloudfiles/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.cloudfiles.CloudFilesApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/cloudfiles/src/test/java/org/jclouds/cloudfiles/CloudFilesApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/cloudfiles/src/test/java/org/jclouds/cloudfiles/CloudFilesApiMetadataTest.java
index ace0f456..2672d7e 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/cloudfiles/src/test/java/org/jclouds/cloudfiles/CloudFilesApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.cloudfiles;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "CloudFilesApiMetadataTest")
+public class CloudFilesApiMetadataTest extends BaseApiMetadataTest {
 
+   public CloudFilesApiMetadataTest() {
+      super(new CloudFilesApiMetadata(), ApiType.BLOBSTORE);
+   }
 }
diff --git a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersApiMetadata.java b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersApiMetadata.java
new file mode 100644
index 0000000..4885acb
--- /dev/null
+++ b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.cloudloadbalancers;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Rackspace Cloud Load Balancers API
+ * 
+ * @author Dan Lo Bianco
+ */
+public class CloudLoadBalancersApiMetadata extends BaseApiMetadata {
+
+   public CloudLoadBalancersApiMetadata() {
+      this(builder()
+            .id("cloudloadbalancers")
+            .type(ApiType.LOADBALANCER)
+            .name("Rackspace Cloud Load Balancers API")
+            .identityName("Username")
+            .credentialName("API Key")
+            .documentation(URI.create("http://docs.rackspacecloud.com/loadbalancers/api/v1.0/clb-devguide/content/ch01.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudLoadBalancersApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudLoadBalancersApiMetadata build() {
+         return new CloudLoadBalancersApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersProviderMetadata.java b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersProviderMetadata.java
deleted file mode 100644
index 700cb84..0000000
--- a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersProviderMetadata.java
+++ /dev/null
@@ -1,69 +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.cloudloadbalancers;
-
-import java.net.URI;
-
-import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-/**
- * Common implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud LoadBalancers.
- * 
- * @author Dan Lo Bianco
- */
-public abstract class CloudLoadBalancersProviderMetadata extends BaseProviderMetadata {
-
-	public CloudLoadBalancersProviderMetadata() {
-		super();
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getType() {
-		return ProviderMetadata.LOADBALANCER_TYPE;
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getIdentityName() {
-		return "Username";
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getCredentialName() {
-		return "API Key";
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public URI getApiDocumentation() {
-		return URI.create("http://docs.rackspacecloud.com/loadbalancers/api/v1.0/clb-devguide/content/ch01.html");
-	}
-
-}
\ No newline at end of file
diff --git a/apis/cloudloadbalancers/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/cloudloadbalancers/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..26850b8
--- /dev/null
+++ b/apis/cloudloadbalancers/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.cloudloadbalancers.CloudLoadBalancersApiMetadata
\ No newline at end of file
diff --git a/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/ProvidersInPropertiesTest.java b/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersApiMetadataTest.java
similarity index 68%
rename from providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/ProvidersInPropertiesTest.java
rename to apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersApiMetadataTest.java
index 1d99bd3..77aa57b 100644
--- a/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/ProvidersInPropertiesTest.java
+++ b/apis/cloudloadbalancers/src/test/java/org/jclouds/cloudloadbalancers/CloudLoadBalancersApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.synaptic.storage;
+package org.jclouds.cloudloadbalancers;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "synaptic-storage") : providers;
-   }
+@Test(groups = "unit", testName = "CloudLoadBalancersApiMetadataTest")
+public class CloudLoadBalancersApiMetadataTest extends BaseApiMetadataTest {
 
+   public CloudLoadBalancersApiMetadataTest() {
+      super(new CloudLoadBalancersApiMetadata(), ApiType.LOADBALANCER);
+   }
 }
diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/CloudServersApiMetadata.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/CloudServersApiMetadata.java
new file mode 100644
index 0000000..c41c0a1
--- /dev/null
+++ b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/CloudServersApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.cloudservers;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Rackspace Cloud Servers API
+ * 
+ * @author Adrian Cole
+ */
+public class CloudServersApiMetadata extends BaseApiMetadata {
+
+   public CloudServersApiMetadata() {
+      this(builder()
+            .id("cloudservers")
+            .type(ApiType.COMPUTE)
+            .name("Rackspace Cloud Servers API")
+            .identityName("Username")
+            .credentialName("API Key")
+            .documentation(URI.create("http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide/content/ch01.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudServersApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudServersApiMetadata build() {
+         return new CloudServersApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/CloudServersProviderMetadata.java b/apis/cloudservers/src/main/java/org/jclouds/cloudservers/CloudServersProviderMetadata.java
deleted file mode 100644
index fde4fea..0000000
--- a/apis/cloudservers/src/main/java/org/jclouds/cloudservers/CloudServersProviderMetadata.java
+++ /dev/null
@@ -1,69 +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.cloudservers;
-
-import java.net.URI;
-
-import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-/**
-* Common implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud Servers.
-* 
-* @author Dan Lo Bianco
-*/
-public abstract class CloudServersProviderMetadata extends BaseProviderMetadata {
-
-	public CloudServersProviderMetadata() {
-		super();
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getType() {
-		return ProviderMetadata.COMPUTE_TYPE;
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getIdentityName() {
-		return "Username";
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getCredentialName() {
-		return "API Key";
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public URI getApiDocumentation() {
-		return URI.create("http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide/content/ch01.html");
-	}
-
-}
\ No newline at end of file
diff --git a/apis/cloudservers/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/cloudservers/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..417ed21
--- /dev/null
+++ b/apis/cloudservers/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.cloudservers.CloudServersApiMetadata
\ No newline at end of file
diff --git a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/CloudServersApiMetadataTest.java
similarity index 69%
copy from providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java
copy to apis/cloudservers/src/test/java/org/jclouds/cloudservers/CloudServersApiMetadataTest.java
index c0d2996..f4d822a 100644
--- a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java
+++ b/apis/cloudservers/src/test/java/org/jclouds/cloudservers/CloudServersApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.cloudonestorage;
+package org.jclouds.cloudservers;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "cloudonestorage") : providers;
-   }
+@Test(groups = "unit", testName = "CloudServersApiMetadataTest")
+public class CloudServersApiMetadataTest extends BaseApiMetadataTest {
 
+   public CloudServersApiMetadataTest() {
+      super(new CloudServersApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/CloudSigmaApiMetadata.java b/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/CloudSigmaApiMetadata.java
new file mode 100644
index 0000000..7d9a7f9
--- /dev/null
+++ b/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/CloudSigmaApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.cloudsigma;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the Cloud Sigma API
+ * 
+ * @author Adrian Cole
+ */
+public class CloudSigmaApiMetadata extends BaseApiMetadata {
+
+   public CloudSigmaApiMetadata() {
+      this(builder()
+            .id("cloudsigma")
+            .type(ApiType.COMPUTE)
+            .name("CloudSigma API")
+            .identityName("Email")
+            .credentialName("Password")
+            .documentation(URI.create("http://cloudsigma.com/en/platform-details/the-api")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudSigmaApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudSigmaApiMetadata build() {
+         return new CloudSigmaApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/cloudsigma/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/cloudsigma/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..768b102
--- /dev/null
+++ b/apis/cloudsigma/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.cloudsigma.CloudSigmaApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaApiMetadataTest.java
index ace0f456..f5a9898 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/CloudSigmaApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.cloudsigma;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "CloudSigmaApiMetadataTest")
+public class CloudSigmaApiMetadataTest extends BaseApiMetadataTest {
 
+   public CloudSigmaApiMetadataTest() {
+      super(new CloudSigmaApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/ProvidersInPropertiesTest.java b/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/ProvidersInPropertiesTest.java
deleted file mode 100644
index 0a93dc1..0000000
--- a/apis/cloudsigma/src/test/java/org/jclouds/cloudsigma/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,47 +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.cloudsigma;
-
-import org.jclouds.compute.util.ComputeServiceUtils;
-import org.jclouds.rest.Providers;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "cloudsigma") : providers;
-   }
-
-   @Test
-   public void testSupportedComputeServiceProviders() {
-      Iterable<String> providers = ComputeServiceUtils.getSupportedProviders();
-      assert Iterables.contains(providers, "cloudsigma") : providers;
-   }
-
-}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackApiMetadata.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackApiMetadata.java
new file mode 100644
index 0000000..d1b30a7
--- /dev/null
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.cloudstack;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Citrix CloudStack API
+ * 
+ * @author Adrian Cole
+ */
+public class CloudStackApiMetadata extends BaseApiMetadata {
+
+   public CloudStackApiMetadata() {
+      this(builder()
+            .id("cloudstack")
+            .type(ApiType.COMPUTE)
+            .name("Citrix CloudStack API")
+            .identityName("API Key")
+            .credentialName("Secret Key")
+            .documentation(URI.create("http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudStackApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudStackApiMetadata build() {
+         return new CloudStackApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/cloudstack/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/cloudstack/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..ad45dce
--- /dev/null
+++ b/apis/cloudstack/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.cloudstack.CloudStackApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackApiMetadataTest.java
index ace0f456..d2d5676 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.cloudstack;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "CloudStackApiMetadataTest")
+public class CloudStackApiMetadataTest extends BaseApiMetadataTest {
 
+   public CloudStackApiMetadataTest() {
+      super(new CloudStackApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatchApiMetadata.java b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatchApiMetadata.java
new file mode 100644
index 0000000..70a8190
--- /dev/null
+++ b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatchApiMetadata.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.cloudwatch;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Amazon's CloudWatch api.
+ * 
+ * @author Adrian Cole
+ */
+public class CloudWatchApiMetadata extends BaseApiMetadata {
+
+   public CloudWatchApiMetadata() {
+      this(builder()
+            .id("cloudwatch")
+            .type(ApiType.MONITOR)
+            .name("Amazon CloudWatch Api")
+            .identityName("Access Key ID")
+            .credentialName("Secret Access Key")
+            .documentation(URI.create("http://docs.amazonwebservices.com/AmazonCloudWatch/latest/APIReference/")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudWatchApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudWatchApiMetadata build() {
+         return new CloudWatchApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatchAsyncClient.java b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatchAsyncClient.java
index c5d36c8..25b9978 100644
--- a/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatchAsyncClient.java
+++ b/apis/cloudwatch/src/main/java/org/jclouds/cloudwatch/CloudWatchAsyncClient.java
@@ -52,7 +52,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = "Version", values = CloudWatchAsyncClient.VERSION)
 @VirtualHost
 public interface CloudWatchAsyncClient {
    public static final String VERSION = "2010-08-01";
diff --git a/apis/cloudwatch/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/cloudwatch/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..de872be
--- /dev/null
+++ b/apis/cloudwatch/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.cloudwatch.CloudWatchApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchApiMetadataTest.java
index ace0f456..e1a72cd 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.cloudwatch;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "CloudWatchApiMetadataTest")
+public class CloudWatchApiMetadataTest extends BaseApiMetadataTest {
 
+   public CloudWatchApiMetadataTest() {
+      super(new CloudWatchApiMetadata(), ApiType.MONITOR);
+   }
 }
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 33d4057..2f4b4f2 100644
--- a/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchAsyncClientTest.java
+++ b/apis/cloudwatch/src/test/java/org/jclouds/cloudwatch/CloudWatchAsyncClientTest.java
@@ -77,7 +77,7 @@
       assertNonPayloadHeadersEqual(request, "Host: monitoring.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-08-01&Action=GetMetricStatistics&Statistics.member.1=Average&Period=60&Namespace=AWS%2FEC2&MetricName=CPUUtilization&StartTime=1970-01-01T02%3A46%3A40Z&EndTime=1970-01-01T02%3A46%3A40Z&Dimensions.member.1.Name=InstanceId&Dimensions.member.1.Value=i-12312313",
+               "Action=GetMetricStatistics&Statistics.member.1=Average&Period=60&Namespace=AWS%2FEC2&MetricName=CPUUtilization&StartTime=1970-01-01T02%3A46%3A40Z&EndTime=1970-01-01T02%3A46%3A40Z&Dimensions.member.1.Name=InstanceId&Dimensions.member.1.Value=i-12312313",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/apis/deltacloud/src/main/java/org/jclouds/deltacloud/DeltacloudApiMetadata.java b/apis/deltacloud/src/main/java/org/jclouds/deltacloud/DeltacloudApiMetadata.java
new file mode 100644
index 0000000..ff12e02
--- /dev/null
+++ b/apis/deltacloud/src/main/java/org/jclouds/deltacloud/DeltacloudApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.deltacloud;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Apache Deltacloud API
+ * 
+ * @author Adrian Cole
+ */
+public class DeltacloudApiMetadata extends BaseApiMetadata {
+
+   public DeltacloudApiMetadata() {
+      this(builder()
+            .id("deltacloud")
+            .type(ApiType.COMPUTE)
+            .name("Apache Deltacloud API")
+            .identityName("Username")
+            .credentialName("Password")
+            .documentation(URI.create("http://deltacloud.apache.org/api.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected DeltacloudApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public DeltacloudApiMetadata build() {
+         return new DeltacloudApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/deltacloud/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/deltacloud/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..834ff5d
--- /dev/null
+++ b/apis/deltacloud/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.deltacloud.DeltacloudApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/deltacloud/src/test/java/org/jclouds/deltacloud/DeltacloudApiMetadataTest.java
similarity index 69%
rename from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
rename to apis/deltacloud/src/test/java/org/jclouds/deltacloud/DeltacloudApiMetadataTest.java
index ace0f456..2dd950d 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/deltacloud/src/test/java/org/jclouds/deltacloud/DeltacloudApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.deltacloud;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "DeltacloudApiMetadataTest")
+public class DeltacloudApiMetadataTest extends BaseApiMetadataTest {
 
+   public DeltacloudApiMetadataTest() {
+      super(new DeltacloudApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/EC2ApiMetadata.java b/apis/ec2/src/main/java/org/jclouds/ec2/EC2ApiMetadata.java
new file mode 100644
index 0000000..6e5120a
--- /dev/null
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/EC2ApiMetadata.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.ec2;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Amazon's EC2 api.
+ * 
+ * @author Adrian Cole
+ */
+public class EC2ApiMetadata extends BaseApiMetadata {
+
+   public EC2ApiMetadata() {
+      this(builder()
+            .id("ec2")
+            .type(ApiType.COMPUTE)
+            .name("Amazon Elastic Compute Cloud (EC2) API")
+            .identityName("Access Key ID")
+            .credentialName("Secret Access Key")
+            .documentation(URI.create("http://docs.amazonwebservices.com/AWSEC2/latest/APIReference")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected EC2ApiMetadata(EC2ApiMetadataBuilder<?> builder) {
+      super(builder);
+   }
+
+   public static class EC2ApiMetadataBuilder<B extends EC2ApiMetadataBuilder<B>> extends Builder<B> {
+
+      @Override
+      public EC2ApiMetadata build() {
+         return new EC2ApiMetadata(this);
+      }
+   }
+
+   private static class EC2ConcreteBuilder extends EC2ApiMetadataBuilder<EC2ConcreteBuilder> {
+
+      @Override
+      public EC2ApiMetadata build() {
+         return new EC2ApiMetadata(this);
+      }
+   }
+
+   private static EC2ConcreteBuilder builder() {
+      return new EC2ConcreteBuilder();
+   }
+
+   @Override
+   public EC2ApiMetadataBuilder<?> toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/EC2PropertiesBuilder.java b/apis/ec2/src/main/java/org/jclouds/ec2/EC2PropertiesBuilder.java
index c31dfad..f117597 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/EC2PropertiesBuilder.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/EC2PropertiesBuilder.java
@@ -21,6 +21,7 @@
 import static org.jclouds.Constants.PROPERTY_API_VERSION;
 import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
 import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
+import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER;
 import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
 import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AUTO_ALLOCATE_ELASTIC_IPS;
 import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT;
@@ -44,6 +45,7 @@
       properties.setProperty(PROPERTY_EC2_AMI_OWNERS, "*");
       properties.setProperty(PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT, "500");
       properties.setProperty(PROPERTY_EC2_AUTO_ALLOCATE_ELASTIC_IPS, "false");
+      properties.setProperty(RESOURCENAME_DELIMITER, "#");
       return properties;
    }
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java
index c62af90..36fe633 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/EC2ComputeService.java
@@ -21,16 +21,16 @@
 import static com.google.common.collect.Iterables.concat;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.transform;
+import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER;
 import static org.jclouds.util.Preconditions2.checkNotEmpty;
 
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
+import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentMap;
 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;
@@ -73,8 +73,9 @@
 import com.google.common.base.Supplier;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableMultimap.Builder;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableMultimap.Builder;
+import com.google.inject.Inject;
 
 /**
  * @author Adrian Cole
@@ -111,6 +112,10 @@
       this.securityGroupMap = securityGroupMap;
    }
 
+   @Inject(optional = true)
+   @Named(RESOURCENAME_DELIMITER)
+   char delimiter = '#';
+
    /**
     * @throws IllegalStateException If the security group was in use
     */
@@ -118,7 +123,7 @@
    void deleteSecurityGroup(String region, String group) {
       checkNotEmpty(region, "region");
       checkNotEmpty(group, "group");
-      String groupName = String.format("jclouds#%s#%s", group, region);
+      String groupName = String.format("jclouds#%s#%s", group, region).replace('#', delimiter);
       if (ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, groupName).size() > 0) {
          logger.debug(">> deleting securityGroup(%s)", groupName);
          ec2Client.getSecurityGroupServices().deleteSecurityGroupInRegion(region, groupName);
@@ -133,11 +138,11 @@
       for (KeyPair keyPair : ec2Client.getKeyPairServices().describeKeyPairsInRegion(region)) {
          if (
          // when the keypair is unique per group
-         keyPair.getKeyName().equals("jclouds#" + group)
-               || keyPair.getKeyName().matches(String.format("jclouds#%s#%s", group, "[0-9a-f]+"))
+         keyPair.getKeyName().equals("jclouds"+ delimiter + group)
+               || keyPair.getKeyName().matches(String.format("jclouds#%s#%s", group, "[0-9a-f]+").replace('#', delimiter))
                // old keypair pattern too verbose as it has an unnecessary
                // region qualifier
-               || keyPair.getKeyName().matches(String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+"))) {
+               || keyPair.getKeyName().matches(String.format("jclouds#%s#%s#%s", group, region, "[0-9a-f]+").replace('#', delimiter))) {
             Set<String> instancesUsingKeyPair = extractIdsFromInstances(filter(concat(ec2Client.getInstanceServices()
                   .describeInstancesInRegion(region)), usingKeyPairAndNotDead(keyPair)));
             if (instancesUsingKeyPair.size() > 0) {
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java
index 687cb84..6a34b28 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java
@@ -35,7 +35,7 @@
 import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier;
 import org.jclouds.ec2.compute.EC2ComputeService;
 import org.jclouds.ec2.compute.domain.RegionAndName;
-import org.jclouds.ec2.compute.functions.RegionAndIdToImage;
+import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
 import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier;
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
index 6281f63..e801bbb 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java
@@ -42,14 +42,14 @@
 import org.jclouds.ec2.compute.EC2ComputeService;
 import org.jclouds.ec2.compute.domain.RegionAndName;
 import org.jclouds.ec2.compute.functions.AddElasticIpsToNodemetadata;
-import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded;
 import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair;
 import org.jclouds.ec2.compute.functions.CredentialsForInstance;
-import org.jclouds.ec2.compute.functions.LoadPublicIpForInstanceOrNull;
-import org.jclouds.ec2.compute.functions.RegionAndIdToImage;
 import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata;
 import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData;
 import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
+import org.jclouds.ec2.compute.loaders.CreateSecurityGroupIfNeeded;
+import org.jclouds.ec2.compute.loaders.LoadPublicIpForInstanceOrNull;
+import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
 import org.jclouds.ec2.compute.options.EC2TemplateOptions;
 import org.jclouds.ec2.compute.predicates.SecurityGroupPresent;
 import org.jclouds.ec2.domain.InstanceState;
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java
index c97094f..2d7e019 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateUniqueKeyPair.java
@@ -19,9 +19,9 @@
 package org.jclouds.ec2.compute.functions;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER;
 
 import javax.annotation.Resource;
-import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
@@ -34,6 +34,7 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Supplier;
+import com.google.inject.Inject;
 
 /**
  * 
@@ -74,8 +75,12 @@
       }
       return keyPair;
    }
-
+   
+   @Inject(optional=true)
+   @Named(RESOURCENAME_DELIMITER) 
+   char delimiter = '#';
+   
    private String getNextName(String region, String group) {
-      return String.format("jclouds#%s#%s#%s", group, region, randomSuffix.get());
+      return String.format("jclouds#%s#%s#%s", group, region, randomSuffix.get()).replace('#', delimiter);
    }
 }
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java
index 2f9922d..be6de1c 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RunningInstanceToNodeMetadata.java
@@ -21,15 +21,16 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Predicates.not;
 import static com.google.common.collect.Iterables.filter;
+import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER;
 
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.Map.Entry;
 
 import javax.annotation.Resource;
-import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.jclouds.collect.Memoized;
@@ -60,11 +61,12 @@
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.inject.Inject;
 
 /**
  * @author Adrian Cole
@@ -195,6 +197,10 @@
       return group;
    }
 
+   @Inject(optional = true)
+   @Named(RESOURCENAME_DELIMITER)
+   char delimiter = '#';
+
    private String parseGroupFrom(final RunningInstance instance, final Set<String> data) {
       String group = null;
       try {
@@ -202,13 +208,13 @@
 
             @Override
             public boolean apply(String input) {
-               return input.startsWith("jclouds#") && input.contains("#" + instance.getRegion());
+               return input.startsWith("jclouds" + delimiter) && input.contains(delimiter + instance.getRegion());
             }
-         })).split("#")[1];
+         })).split(delimiter + "")[1];
       } catch (NoSuchElementException e) {
          logger.debug("no group parsed from %s's data: %s", instance.getId(), data);
       } catch (IllegalArgumentException e) {
-         logger.debug("too many groups match %s; %s's data: %s", "jclouds#", instance.getId(), data);
+         logger.debug("too many groups match %s%s; %s's data: %s", "jclouds", delimiter, instance.getId(), data);
       }
       return group;
    }
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java
similarity index 95%
rename from apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java
rename to apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java
index 5fa6cc7..79c175a 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeeded.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.ec2.compute.functions;
+package org.jclouds.ec2.compute.loaders;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -92,13 +92,13 @@
       }
    }
 
-   private void createIngressRuleForTCPPort(String region, String name, int port) {
+   protected void createIngressRuleForTCPPort(String region, String name, int port) {
       logger.debug(">> authorizing securityGroup region(%s) name(%s) port(%s)", region, name, port);
       securityClient.authorizeSecurityGroupIngressInRegion(region, name, IpProtocol.TCP, port, port, "0.0.0.0/0");
       logger.debug("<< authorized securityGroup(%s)", name);
    }
 
-   private void authorizeGroupToItself(String region, String name) {
+   protected void authorizeGroupToItself(String region, String name) {
       logger.debug(">> authorizing securityGroup region(%s) name(%s) permission to itself", region, name);
       String myOwnerId = Iterables.get(securityClient.describeSecurityGroupsInRegion(region, name), 0).getOwnerId();
       securityClient.authorizeSecurityGroupIngressInRegion(region, name, new UserIdGroupPair(myOwnerId, name));
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/LoadPublicIpForInstanceOrNull.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/LoadPublicIpForInstanceOrNull.java
similarity index 97%
rename from apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/LoadPublicIpForInstanceOrNull.java
rename to apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/LoadPublicIpForInstanceOrNull.java
index 4e47ebe..1033814 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/LoadPublicIpForInstanceOrNull.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/LoadPublicIpForInstanceOrNull.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.ec2.compute.functions;
+package org.jclouds.ec2.compute.loaders;
 
 import java.util.NoSuchElementException;
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RegionAndIdToImage.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/RegionAndIdToImage.java
similarity index 95%
rename from apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RegionAndIdToImage.java
rename to apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/RegionAndIdToImage.java
index 1abd546..e127f43 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/RegionAndIdToImage.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/loaders/RegionAndIdToImage.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.ec2.compute.functions;
+package org.jclouds.ec2.compute.loaders;
 
 import static org.jclouds.ec2.options.DescribeImagesOptions.Builder.imageIds;
 
@@ -29,6 +29,7 @@
 import org.jclouds.compute.domain.Image;
 import org.jclouds.ec2.EC2Client;
 import org.jclouds.ec2.compute.domain.RegionAndName;
+import org.jclouds.ec2.compute.functions.EC2ImageParser;
 import org.jclouds.logging.Logger;
 
 import com.google.common.cache.CacheLoader;
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
index 534c26d..80920cd 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
@@ -21,13 +21,13 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER;
 import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey;
 import static org.jclouds.crypto.SshKeys.sha1PrivateKey;
 
 import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 
-import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Provider;
 import javax.inject.Singleton;
@@ -47,6 +47,7 @@
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
+import com.google.inject.Inject;
 
 /**
  * 
@@ -159,12 +160,16 @@
       return keyPair.getKeyName();
    }
 
+   @Inject(optional = true)
+   @Named(RESOURCENAME_DELIMITER)
+   char delimiter = '#';
+
    @VisibleForTesting
    public Set<String> getSecurityGroupsForTagAndOptions(String region, @Nullable String group, TemplateOptions options) {
       Builder<String> groups = ImmutableSet.<String> builder();
 
       if (group != null) {
-         String markerGroup = String.format("jclouds#%s#%s", group, region);
+         String markerGroup = String.format("jclouds#%s#%s", group, region).replace('#', delimiter);
          groups.add(markerGroup);
 
          RegionNameAndIngressRules regionNameAndIngessRulesForMarkerGroup;
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/AMIAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/AMIAsyncClient.java
index 19e87c0..63cc190 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/AMIAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/AMIAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Map;
 import java.util.Set;
@@ -29,12 +28,11 @@
 import javax.ws.rs.Path;
 
 import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.binders.BindUserGroupsToIndexedFormParams;
 import org.jclouds.ec2.binders.BindUserIdsToIndexedFormParams;
 import org.jclouds.ec2.domain.Image;
-import org.jclouds.ec2.domain.Image.EbsBlockDevice;
 import org.jclouds.ec2.domain.Permission;
+import org.jclouds.ec2.domain.Image.EbsBlockDevice;
 import org.jclouds.ec2.options.CreateImageOptions;
 import org.jclouds.ec2.options.DescribeImagesOptions;
 import org.jclouds.ec2.options.RegisterImageBackedByEbsOptions;
@@ -63,7 +61,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION)
 @VirtualHost
 public interface AMIAsyncClient {
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionAsyncClient.java
index d3a9d67..4afb621 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.net.URI;
 import java.util.Map;
@@ -29,7 +28,6 @@
 import javax.ws.rs.Path;
 
 import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.domain.AvailabilityZoneInfo;
 import org.jclouds.ec2.options.DescribeAvailabilityZonesOptions;
 import org.jclouds.ec2.options.DescribeRegionsOptions;
@@ -54,7 +52,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION)
 @VirtualHost
 public interface AvailabilityZoneAndRegionAsyncClient {
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/ElasticBlockStoreAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/ElasticBlockStoreAsyncClient.java
index 2e7ff1e..3951ecf 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/ElasticBlockStoreAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/ElasticBlockStoreAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -28,7 +27,6 @@
 import javax.ws.rs.Path;
 
 import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.binders.BindUserGroupsToIndexedFormParams;
 import org.jclouds.ec2.binders.BindUserIdsToIndexedFormParams;
 import org.jclouds.ec2.binders.BindVolumeIdsToIndexedFormParams;
@@ -67,7 +65,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION)
 @VirtualHost
 public interface ElasticBlockStoreAsyncClient {
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/ElasticIPAddressAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/ElasticIPAddressAsyncClient.java
index b435b9f..81f93d7 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/ElasticIPAddressAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/ElasticIPAddressAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -28,7 +27,6 @@
 import javax.ws.rs.Path;
 
 import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.binders.BindPublicIpsToIndexedFormParams;
 import org.jclouds.ec2.domain.PublicIpInstanceIdPair;
 import org.jclouds.ec2.xml.AllocateAddressResponseHandler;
@@ -53,7 +51,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION)
 @VirtualHost
 public interface ElasticIPAddressAsyncClient {
 
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 81917d4..af5fd3a 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
@@ -19,7 +19,6 @@
 package org.jclouds.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Map;
 import java.util.Set;
@@ -29,7 +28,6 @@
 import javax.ws.rs.Path;
 
 import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.binders.BindBlockDeviceMappingToIndexedFormParams;
 import org.jclouds.ec2.binders.BindInstanceIdsToIndexedFormParams;
 import org.jclouds.ec2.binders.IfNotNullBindAvailabilityZoneToFormParam;
@@ -70,7 +68,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION)
 @VirtualHost
 public interface InstanceAsyncClient {
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/KeyPairAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/KeyPairAsyncClient.java
index f243b7f..df41dd0 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/KeyPairAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/KeyPairAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -28,7 +27,6 @@
 import javax.ws.rs.Path;
 
 import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.binders.BindKeyNamesToIndexedFormParams;
 import org.jclouds.ec2.domain.KeyPair;
 import org.jclouds.ec2.xml.DescribeKeyPairsResponseHandler;
@@ -53,7 +51,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION)
 @VirtualHost
 public interface KeyPairAsyncClient {
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java
index 15ee188..d40d847 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/SecurityGroupAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -28,7 +27,6 @@
 import javax.ws.rs.Path;
 
 import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.binders.BindGroupNamesToIndexedFormParams;
 import org.jclouds.ec2.binders.BindUserIdGroupPairToSourceSecurityGroupFormParams;
 import org.jclouds.ec2.domain.IpProtocol;
@@ -56,7 +54,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION)
 @VirtualHost
 public interface SecurityGroupAsyncClient {
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java
index b354836..04ee77e 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/services/WindowsAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -28,7 +27,6 @@
 import javax.ws.rs.Path;
 
 import org.jclouds.aws.filters.FormSigner;
-import org.jclouds.ec2.EC2AsyncClient;
 import org.jclouds.ec2.binders.BindBundleIdsToIndexedFormParams;
 import org.jclouds.ec2.binders.BindS3UploadPolicyAndSignature;
 import org.jclouds.ec2.domain.BundleTask;
@@ -57,7 +55,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION)
 @VirtualHost
 public interface WindowsAsyncClient {
 
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeImagesResponseHandler.java b/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeImagesResponseHandler.java
index e681266..2e92af7 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeImagesResponseHandler.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/xml/DescribeImagesResponseHandler.java
@@ -110,7 +110,8 @@
    public void endElement(String uri, String name, String qName) {
       if (qName.equals("architecture")) {
          architecture = Architecture.fromValue(currentText.toString().trim());
-      } else if (qName.equals("name")) {
+      // Nova Diablo uses the wrong name for this field
+      } else if (qName.equals("name") || qName.equals("displayName")) {
          this.name = currentText.toString().trim();
       } else if (qName.equals("description")) {
          description = currentText.toString().trim();
diff --git a/apis/ec2/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/ec2/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..dce15a1
--- /dev/null
+++ b/apis/ec2/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.ec2.EC2ApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/EC2ApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/ec2/src/test/java/org/jclouds/ec2/EC2ApiMetadataTest.java
index ace0f456..14f62cd 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/EC2ApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.ec2;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "EC2ApiMetadataTest")
+public class EC2ApiMetadataTest extends BaseApiMetadataTest {
 
+   public EC2ApiMetadataTest() {
+      super(new EC2ApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModuleTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModuleTest.java
index 96b898a..9c0b5f5 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModuleTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModuleTest.java
@@ -25,7 +25,7 @@
 
 import org.jclouds.compute.domain.Image;
 import org.jclouds.ec2.compute.domain.RegionAndName;
-import org.jclouds.ec2.compute.functions.RegionAndIdToImage;
+import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
 import org.jclouds.rest.AuthorizationException;
 import org.testng.annotations.Test;
 
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeededTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java
similarity index 98%
rename from apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeededTest.java
rename to apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java
index cba89fe..f4f683e 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeededTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/CreateSecurityGroupIfNeededTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.ec2.compute.functions;
+package org.jclouds.ec2.compute.loaders;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/LoadPublicIpForInstanceOrNullTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/LoadPublicIpForInstanceOrNullTest.java
similarity index 98%
rename from apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/LoadPublicIpForInstanceOrNullTest.java
rename to apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/LoadPublicIpForInstanceOrNullTest.java
index fa9c47b..b276549 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/LoadPublicIpForInstanceOrNullTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/LoadPublicIpForInstanceOrNullTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.ec2.compute.functions;
+package org.jclouds.ec2.compute.loaders;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RegionAndIdToImageTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/RegionAndIdToImageTest.java
similarity index 97%
rename from apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RegionAndIdToImageTest.java
rename to apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/RegionAndIdToImageTest.java
index 3efd76d..f94fd5c 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/RegionAndIdToImageTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/loaders/RegionAndIdToImageTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.ec2.compute.functions;
+package org.jclouds.ec2.compute.loaders;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
@@ -33,6 +33,7 @@
 import org.jclouds.compute.domain.Image;
 import org.jclouds.ec2.EC2Client;
 import org.jclouds.ec2.compute.domain.RegionAndName;
+import org.jclouds.ec2.compute.functions.EC2ImageParser;
 import org.jclouds.ec2.services.AMIClient;
 import org.jclouds.rest.ResourceNotFoundException;
 import org.testng.annotations.Test;
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/AMIAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/AMIAsyncClientTest.java
index 6502611..ac52311 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/AMIAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/AMIAsyncClientTest.java
@@ -58,7 +58,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=CreateImage&InstanceId=instanceId&Name=name",
+      assertPayloadEquals(request, "Action=CreateImage&InstanceId=instanceId&Name=name",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ParseSax.class);
       assertSaxResponseParserClassEquals(method, ImageIdHandler.class);
@@ -77,7 +77,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=CreateImage&InstanceId=instanceId&Name=name&Description=description&NoReboot=true",
+               "Action=CreateImage&InstanceId=instanceId&Name=name&Description=description&NoReboot=true",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -94,7 +94,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeImages", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeImages", "application/x-www-form-urlencoded",
                false);
       filter.filter(request);
       assertPayloadEquals(
@@ -119,7 +119,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=DescribeImages&ExecutableBy=me&Owner.1=fred&Owner.2=nancy&ImageId.1=1&ImageId.2=2",
+               "Action=DescribeImages&ExecutableBy=me&Owner.1=fred&Owner.2=nancy&ImageId.1=1&ImageId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -135,7 +135,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DeregisterImage&ImageId=imageId",
+      assertPayloadEquals(request, "Action=DeregisterImage&ImageId=imageId",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -152,7 +152,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=RegisterImage&ImageLocation=pathToManifest&Name=name",
+      assertPayloadEquals(request, "Action=RegisterImage&ImageLocation=pathToManifest&Name=name",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ParseSax.class);
       assertSaxResponseParserClassEquals(method, ImageIdHandler.class);
@@ -171,7 +171,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=RegisterImage&ImageLocation=pathToManifest&Name=name&Description=description",
+               "Action=RegisterImage&ImageLocation=pathToManifest&Name=name&Description=description",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -190,7 +190,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=RegisterImage&RootDeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Name=imageName",
+               "Action=RegisterImage&RootDeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Name=imageName",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ParseSax.class);
       assertSaxResponseParserClassEquals(method, ImageIdHandler.class);
@@ -210,7 +210,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=RegisterImage&RootDeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Name=imageName&Description=description&BlockDeviceMapping.1.Ebs.DeleteOnTermination=false&BlockDeviceMapping.1.DeviceName=%2Fdev%2Fdevice&BlockDeviceMapping.1.Ebs.SnapshotId=snapshot&BlockDeviceMapping.2.Ebs.DeleteOnTermination=false&BlockDeviceMapping.2.DeviceName=%2Fdev%2Fnewdevice&BlockDeviceMapping.2.VirtualName=newblock&BlockDeviceMapping.2.Ebs.VolumeSize=100",
+               "Action=RegisterImage&RootDeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Name=imageName&Description=description&BlockDeviceMapping.1.Ebs.DeleteOnTermination=false&BlockDeviceMapping.1.DeviceName=%2Fdev%2Fdevice&BlockDeviceMapping.1.Ebs.SnapshotId=snapshot&BlockDeviceMapping.2.Ebs.DeleteOnTermination=false&BlockDeviceMapping.2.DeviceName=%2Fdev%2Fnewdevice&BlockDeviceMapping.2.VirtualName=newblock&BlockDeviceMapping.2.Ebs.VolumeSize=100",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -228,7 +228,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DescribeImageAttribute&Attribute=blockDeviceMapping&ImageId=imageId",
+               "Action=DescribeImageAttribute&Attribute=blockDeviceMapping&ImageId=imageId",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -245,7 +245,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DescribeImageAttribute&Attribute=launchPermission&ImageId=imageId",
+               "Action=DescribeImageAttribute&Attribute=launchPermission&ImageId=imageId",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -265,7 +265,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=ModifyImageAttribute&OperationType=add&Attribute=launchPermission&ImageId=imageId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
+               "Action=ModifyImageAttribute&OperationType=add&Attribute=launchPermission&ImageId=imageId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
                "application/x-www-form-urlencoded", false);
       filter.filter(request);
       assertPayloadEquals(
@@ -290,7 +290,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=ModifyImageAttribute&OperationType=remove&Attribute=launchPermission&ImageId=imageId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
+               "Action=ModifyImageAttribute&OperationType=remove&Attribute=launchPermission&ImageId=imageId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
@@ -307,7 +307,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=ResetImageAttribute&Attribute=launchPermission&ImageId=imageId",
+               "Action=ResetImageAttribute&Attribute=launchPermission&ImageId=imageId",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionAsyncClientTest.java
index a828e10..452c66c 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/AvailabilityZoneAndRegionAsyncClientTest.java
@@ -55,7 +55,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-west-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-west-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeAvailabilityZones",
+      assertPayloadEquals(request, "Action=DescribeAvailabilityZones",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -73,7 +73,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2010-06-15&Action=DescribeAvailabilityZones&ZoneName.1=us-east-1a&ZoneName.2=us-east-1b",
+            "Action=DescribeAvailabilityZones&ZoneName.1=us-east-1a&ZoneName.2=us-east-1b",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -90,7 +90,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeRegions", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeRegions", "application/x-www-form-urlencoded",
             false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -108,7 +108,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2010-06-15&Action=DescribeRegions&RegionName.1=us-east-1&RegionName.2=us-west-1",
+            "Action=DescribeRegions&RegionName.1=us-east-1&RegionName.2=us-west-1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticBlockStoreAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticBlockStoreAsyncClientTest.java
index a6bab3a..8223af5 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticBlockStoreAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticBlockStoreAsyncClientTest.java
@@ -61,7 +61,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=CreateVolume&AvailabilityZone=us-east-1a&Size=20",
+      assertPayloadEquals(request, "Action=CreateVolume&AvailabilityZone=us-east-1a&Size=20",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -79,7 +79,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=CreateVolume&AvailabilityZone=us-east-1a&SnapshotId=snapshotId",
+               "Action=CreateVolume&AvailabilityZone=us-east-1a&SnapshotId=snapshotId",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -97,7 +97,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=CreateVolume&AvailabilityZone=us-east-1a&SnapshotId=snapshotId&Size=15",
+               "Action=CreateVolume&AvailabilityZone=us-east-1a&SnapshotId=snapshotId&Size=15",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -113,7 +113,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DeleteVolume&VolumeId=id",
+      assertPayloadEquals(request, "Action=DeleteVolume&VolumeId=id",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -130,7 +130,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeVolumes", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeVolumes", "application/x-www-form-urlencoded",
                false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -147,7 +147,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeVolumes&VolumeId.1=1&VolumeId.2=2",
+      assertPayloadEquals(request, "Action=DescribeVolumes&VolumeId.1=1&VolumeId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -165,7 +165,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=AttachVolume&InstanceId=instanceId&VolumeId=id&Device=%2Fdevice",
+               "Action=AttachVolume&InstanceId=instanceId&VolumeId=id&Device=%2Fdevice",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -182,7 +182,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DetachVolume&Force=false&VolumeId=id",
+      assertPayloadEquals(request, "Action=DetachVolume&Force=false&VolumeId=id",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -201,7 +201,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DetachVolume&Force=true&VolumeId=id&InstanceId=instanceId&Device=%2Fdevice",
+               "Action=DetachVolume&Force=true&VolumeId=id&InstanceId=instanceId&Device=%2Fdevice",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -218,7 +218,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=CreateSnapshot&VolumeId=volumeId",
+      assertPayloadEquals(request, "Action=CreateSnapshot&VolumeId=volumeId",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -237,7 +237,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=CreateSnapshot&VolumeId=volumeId&Description=description",
+               "Action=CreateSnapshot&VolumeId=volumeId&Description=description",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -254,7 +254,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeSnapshots", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeSnapshots", "application/x-www-form-urlencoded",
                false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -274,7 +274,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=DescribeSnapshots&Owner.1=o1&Owner.2=o2&RestorableBy.1=r1&RestorableBy.2=r2&SnapshotId.1=s1&SnapshotId.2=s2",
+               "Action=DescribeSnapshots&Owner.1=o1&Owner.2=o2&RestorableBy.1=r1&RestorableBy.2=r2&SnapshotId.1=s1&SnapshotId.2=s2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -293,7 +293,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=DescribeSnapshotAttribute&Attribute=createVolumePermission&SnapshotId=snapshotId",
+               "Action=DescribeSnapshotAttribute&Attribute=createVolumePermission&SnapshotId=snapshotId",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -313,7 +313,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=ModifySnapshotAttribute&OperationType=add&Attribute=createVolumePermission&SnapshotId=snapshotId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
+               "Action=ModifySnapshotAttribute&OperationType=add&Attribute=createVolumePermission&SnapshotId=snapshotId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -334,7 +334,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=ModifySnapshotAttribute&OperationType=remove&Attribute=createVolumePermission&SnapshotId=snapshotId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
+               "Action=ModifySnapshotAttribute&OperationType=remove&Attribute=createVolumePermission&SnapshotId=snapshotId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
@@ -353,7 +353,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=ResetSnapshotAttribute&Attribute=createVolumePermission&SnapshotId=snapshotId",
+               "Action=ResetSnapshotAttribute&Attribute=createVolumePermission&SnapshotId=snapshotId",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticIPAddressAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticIPAddressAsyncClientTest.java
index 9387bd9..b46664e 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticIPAddressAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/ElasticIPAddressAsyncClientTest.java
@@ -49,7 +49,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DisassociateAddress&PublicIp=127.0.0.1",
+      assertPayloadEquals(request, "Action=DisassociateAddress&PublicIp=127.0.0.1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -66,7 +66,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=AssociateAddress&InstanceId=me&PublicIp=127.0.0.1",
+      assertPayloadEquals(request, "Action=AssociateAddress&InstanceId=me&PublicIp=127.0.0.1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -82,7 +82,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=ReleaseAddress&PublicIp=127.0.0.1",
+      assertPayloadEquals(request, "Action=ReleaseAddress&PublicIp=127.0.0.1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -99,7 +99,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeAddresses&PublicIp.1=127.0.0.1",
+      assertPayloadEquals(request, "Action=DescribeAddresses&PublicIp.1=127.0.0.1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -115,7 +115,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=AllocateAddress", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=AllocateAddress", "application/x-www-form-urlencoded",
             false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
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 732bd4d..96d00c6 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
@@ -60,7 +60,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeInstances", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeInstances", "application/x-www-form-urlencoded",
                false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -76,7 +76,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeInstances&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=DescribeInstances&InstanceId.1=1&InstanceId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -93,7 +93,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=TerminateInstances&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=TerminateInstances&InstanceId.1=1&InstanceId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -111,11 +111,11 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       try {
-         assertPayloadEquals(request, "Version=2010-06-15&Action=RunInstances&ImageId=ami-voo&MinCount=1&MaxCount=1",
+         assertPayloadEquals(request, "Action=RunInstances&ImageId=ami-voo&MinCount=1&MaxCount=1",
                   "application/x-www-form-urlencoded", false);
       } catch (AssertionError e) {
          // mvn 3.0 osx 10.6.5 somehow sorts differently
-         assertPayloadEquals(request, "Version=2010-06-15&Action=RunInstances&ImageId=ami-voo&MaxCount=1&MinCount=1",
+         assertPayloadEquals(request, "Action=RunInstances&ImageId=ami-voo&MaxCount=1&MinCount=1",
                   "application/x-www-form-urlencoded", false);
       }
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -136,13 +136,13 @@
       try {
          assertPayloadEquals(
                   request,
-                  "Version=2010-06-15&Action=RunInstances&ImageId=ami-voo&MinCount=1&MaxCount=5&KernelId=kernelId&SecurityGroup.1=group1&SecurityGroup.2=group2&Placement.AvailabilityZone=eu-west-1a",
+                  "Action=RunInstances&ImageId=ami-voo&MinCount=1&MaxCount=5&KernelId=kernelId&SecurityGroup.1=group1&SecurityGroup.2=group2&Placement.AvailabilityZone=eu-west-1a",
                   "application/x-www-form-urlencoded", false);
       } catch (AssertionError e) {
          // mvn 3.0 osx 10.6.5 somehow sorts differently
          assertPayloadEquals(
                   request,
-                  "Version=2010-06-15&Action=RunInstances&ImageId=ami-voo&MaxCount=5&MinCount=1&KernelId=kernelId&SecurityGroup.1=group1&SecurityGroup.2=group2&Placement.AvailabilityZone=eu-west-1a",
+                  "Action=RunInstances&ImageId=ami-voo&MaxCount=5&MinCount=1&KernelId=kernelId&SecurityGroup.1=group1&SecurityGroup.2=group2&Placement.AvailabilityZone=eu-west-1a",
                   "application/x-www-form-urlencoded", false);
       }
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -159,7 +159,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=StopInstances&Force=true&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=StopInstances&Force=true&InstanceId.1=1&InstanceId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -176,7 +176,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=RebootInstances&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=RebootInstances&InstanceId.1=1&InstanceId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -193,7 +193,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=StartInstances&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=StartInstances&InstanceId.1=1&InstanceId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -210,7 +210,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DescribeInstanceAttribute&Attribute=userData&InstanceId=1",
+               "Action=DescribeInstanceAttribute&Attribute=userData&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -228,7 +228,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DescribeInstanceAttribute&Attribute=rootDeviceName&InstanceId=1",
+               "Action=DescribeInstanceAttribute&Attribute=rootDeviceName&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -245,7 +245,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DescribeInstanceAttribute&Attribute=ramdisk&InstanceId=1",
+               "Action=DescribeInstanceAttribute&Attribute=ramdisk&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -264,7 +264,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DescribeInstanceAttribute&Attribute=disableApiTermination&InstanceId=1",
+               "Action=DescribeInstanceAttribute&Attribute=disableApiTermination&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -280,7 +280,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeInstanceAttribute&Attribute=kernel&InstanceId=1",
+      assertPayloadEquals(request, "Action=DescribeInstanceAttribute&Attribute=kernel&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -298,7 +298,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DescribeInstanceAttribute&Attribute=instanceType&InstanceId=1",
+               "Action=DescribeInstanceAttribute&Attribute=instanceType&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -318,7 +318,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=DescribeInstanceAttribute&Attribute=instanceInitiatedShutdownBehavior&InstanceId=1",
+               "Action=DescribeInstanceAttribute&Attribute=instanceInitiatedShutdownBehavior&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -337,7 +337,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=DescribeInstanceAttribute&Attribute=blockDeviceMapping&InstanceId=1",
+               "Action=DescribeInstanceAttribute&Attribute=blockDeviceMapping&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -355,7 +355,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=ModifyInstanceAttribute&Attribute=userData&Value=dGVzdA%3D%3D&InstanceId=1",
+               "Action=ModifyInstanceAttribute&Attribute=userData&Value=dGVzdA%3D%3D&InstanceId=1",
                "application/x-www-form-urlencoded", false);
       filter.filter(request);// ensure encoding worked properly
       assertPayloadEquals(
@@ -377,7 +377,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=ModifyInstanceAttribute&Attribute=ramdisk&Value=test&InstanceId=1",
+               "Action=ModifyInstanceAttribute&Attribute=ramdisk&Value=test&InstanceId=1",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
@@ -394,7 +394,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=ModifyInstanceAttribute&Attribute=kernel&Value=test&InstanceId=1",
+               "Action=ModifyInstanceAttribute&Attribute=kernel&Value=test&InstanceId=1",
                "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
@@ -413,7 +413,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=ModifyInstanceAttribute&Attribute=disableApiTermination&Value=true&InstanceId=1",
+               "Action=ModifyInstanceAttribute&Attribute=disableApiTermination&Value=true&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -431,7 +431,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-               "Version=2010-06-15&Action=ModifyInstanceAttribute&Attribute=instanceType&Value=c1.medium&InstanceId=1",
+               "Action=ModifyInstanceAttribute&Attribute=instanceType&Value=c1.medium&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -451,7 +451,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=ModifyInstanceAttribute&Attribute=instanceInitiatedShutdownBehavior&Value=terminate&InstanceId=1",
+               "Action=ModifyInstanceAttribute&Attribute=instanceInitiatedShutdownBehavior&Value=terminate&InstanceId=1",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -474,7 +474,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=ModifyInstanceAttribute&InstanceId=1&BlockDeviceMapping.1.Ebs.VolumeId=vol-test1&BlockDeviceMapping.1.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.1.Ebs.DeleteOnTermination=true",
+               "Action=ModifyInstanceAttribute&InstanceId=1&BlockDeviceMapping.1.Ebs.VolumeId=vol-test1&BlockDeviceMapping.1.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.1.Ebs.DeleteOnTermination=true",
                "application/x-www-form-urlencoded", false);
       filter.filter(request);// ensure encoding worked properly
       assertPayloadEquals(
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/KeyPairAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/KeyPairAsyncClientTest.java
index 45249c9..3a2fba3 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/KeyPairAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/KeyPairAsyncClientTest.java
@@ -47,7 +47,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DeleteKeyPair&KeyName=mykey",
+      assertPayloadEquals(request, "Action=DeleteKeyPair&KeyName=mykey",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -64,7 +64,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeKeyPairs", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeKeyPairs", "application/x-www-form-urlencoded",
             false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -81,7 +81,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeKeyPairs&KeyName.1=1&KeyName.2=2",
+      assertPayloadEquals(request, "Action=DescribeKeyPairs&KeyName.1=1&KeyName.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java
index 0f7d412..1236506 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/SecurityGroupAsyncClientTest.java
@@ -51,7 +51,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DeleteSecurityGroup&GroupName=name",
+      assertPayloadEquals(request, "Action=DeleteSecurityGroup&GroupName=name",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -69,7 +69,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2010-06-15&Action=CreateSecurityGroup&GroupDescription=description&GroupName=name",
+            "Action=CreateSecurityGroup&GroupDescription=description&GroupName=name",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -86,7 +86,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeSecurityGroups",
+      assertPayloadEquals(request, "Action=DescribeSecurityGroups",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -103,7 +103,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeSecurityGroups&GroupName.1=1&GroupName.2=2",
+      assertPayloadEquals(request, "Action=DescribeSecurityGroups&GroupName.1=1&GroupName.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -123,7 +123,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2010-06-15&Action=AuthorizeSecurityGroupIngress&GroupName=group&SourceSecurityGroupOwnerId=sourceUser&SourceSecurityGroupName=sourceGroup",
+            "Action=AuthorizeSecurityGroupIngress&GroupName=group&SourceSecurityGroupOwnerId=sourceUser&SourceSecurityGroupName=sourceGroup",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -143,13 +143,13 @@
       try {
          assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=AuthorizeSecurityGroupIngress&CidrIp=0.0.0.0%2F0&IpProtocol=tcp&GroupName=group&FromPort=6000&ToPort=7000",
+               "Action=AuthorizeSecurityGroupIngress&CidrIp=0.0.0.0%2F0&IpProtocol=tcp&GroupName=group&FromPort=6000&ToPort=7000",
                "application/x-www-form-urlencoded", false);
       } catch (AssertionError e) {
          // mvn 3.0 osx 10.6.5 somehow sorts differently
          assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=AuthorizeSecurityGroupIngress&CidrIp=0.0.0.0%2F0&IpProtocol=tcp&GroupName=group&ToPort=7000&FromPort=6000",
+               "Action=AuthorizeSecurityGroupIngress&CidrIp=0.0.0.0%2F0&IpProtocol=tcp&GroupName=group&ToPort=7000&FromPort=6000",
                "application/x-www-form-urlencoded", false);
       }
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -169,7 +169,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2010-06-15&Action=RevokeSecurityGroupIngress&GroupName=group&SourceSecurityGroupOwnerId=sourceUser&SourceSecurityGroupName=sourceGroup",
+            "Action=RevokeSecurityGroupIngress&GroupName=group&SourceSecurityGroupOwnerId=sourceUser&SourceSecurityGroupName=sourceGroup",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -189,13 +189,13 @@
       try {
          assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=RevokeSecurityGroupIngress&CidrIp=0.0.0.0%2F0&IpProtocol=tcp&GroupName=group&FromPort=6000&ToPort=7000",
+               "Action=RevokeSecurityGroupIngress&CidrIp=0.0.0.0%2F0&IpProtocol=tcp&GroupName=group&FromPort=6000&ToPort=7000",
                "application/x-www-form-urlencoded", false);
       } catch (AssertionError e) {
          // mvn 3.0 osx 10.6.5 somehow sorts differently
          assertPayloadEquals(
                request,
-               "Version=2010-06-15&Action=RevokeSecurityGroupIngress&CidrIp=0.0.0.0%2F0&IpProtocol=tcp&GroupName=group&ToPort=7000&FromPort=6000",
+               "Action=RevokeSecurityGroupIngress&CidrIp=0.0.0.0%2F0&IpProtocol=tcp&GroupName=group&ToPort=7000&FromPort=6000",
                "application/x-www-form-urlencoded", false);
       }
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsAsyncClientTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsAsyncClientTest.java
index 859d48f..9c3da59 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsAsyncClientTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/services/WindowsAsyncClientTest.java
@@ -53,7 +53,7 @@
                         "my-bucket",
                         "{\"expiration\": \"2008-08-30T08:49:09Z\",\"conditions\": [{\"bucket\": \"my-bucket\"},[\"starts-with\", \"$key\", \"my-new-image\"]]}");
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
-      String payload = "Version=2010-06-15&Action=BundleInstance&Storage.S3.Prefix=winami&InstanceId=i-e468cd8d&Storage.S3.Bucket=my-bucket&Storage.S3.UploadPolicy=eyJleHBpcmF0aW9uIjogIjIwMDgtMDgtMzBUMDg6NDk6MDlaIiwiY29uZGl0aW9ucyI6IFt7ImJ1Y2tldCI6ICJteS1idWNrZXQifSxbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAibXktbmV3LWltYWdlIl1dfQ%3D%3D&Storage.S3.UploadPolicySignature=ih%2FiohGe0A7y4QVRbKaq6BZShzUsmBEJEa9AdFbxM6Y%3D";
+      String payload = "Action=BundleInstance&Storage.S3.Prefix=winami&InstanceId=i-e468cd8d&Storage.S3.Bucket=my-bucket&Storage.S3.UploadPolicy=eyJleHBpcmF0aW9uIjogIjIwMDgtMDgtMzBUMDg6NDk6MDlaIiwiY29uZGl0aW9ucyI6IFt7ImJ1Y2tldCI6ICJteS1idWNrZXQifSxbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAibXktbmV3LWltYWdlIl1dfQ%3D%3D&Storage.S3.UploadPolicySignature=ih%2FiohGe0A7y4QVRbKaq6BZShzUsmBEJEa9AdFbxM6Y%3D";
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request, payload, "application/x-www-form-urlencoded", false);
 
@@ -77,7 +77,7 @@
                         "{\"expiration\": \"2008-08-30T08:49:09Z\",\"conditions\": [{\"bucket\": \"my-bucket\"},[\"starts-with\", \"$key\", \"my-new-image\"]]}",
                         BundleInstanceS3StorageOptions.Builder.bucketOwnedBy("10QMXFEV71ZS32XQFTR2"));
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
-      String payload = "Version=2010-06-15&Action=BundleInstance&Storage.S3.Prefix=winami&InstanceId=i-e468cd8d&Storage.S3.Bucket=my-bucket&Storage.S3.AWSAccessKeyId=10QMXFEV71ZS32XQFTR2&Storage.S3.UploadPolicy=eyJleHBpcmF0aW9uIjogIjIwMDgtMDgtMzBUMDg6NDk6MDlaIiwiY29uZGl0aW9ucyI6IFt7ImJ1Y2tldCI6ICJteS1idWNrZXQifSxbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAibXktbmV3LWltYWdlIl1dfQ%3D%3D&Storage.S3.UploadPolicySignature=ih%2FiohGe0A7y4QVRbKaq6BZShzUsmBEJEa9AdFbxM6Y%3D";
+      String payload = "Action=BundleInstance&Storage.S3.Prefix=winami&InstanceId=i-e468cd8d&Storage.S3.Bucket=my-bucket&Storage.S3.AWSAccessKeyId=10QMXFEV71ZS32XQFTR2&Storage.S3.UploadPolicy=eyJleHBpcmF0aW9uIjogIjIwMDgtMDgtMzBUMDg6NDk6MDlaIiwiY29uZGl0aW9ucyI6IFt7ImJ1Y2tldCI6ICJteS1idWNrZXQifSxbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAibXktbmV3LWltYWdlIl1dfQ%3D%3D&Storage.S3.UploadPolicySignature=ih%2FiohGe0A7y4QVRbKaq6BZShzUsmBEJEa9AdFbxM6Y%3D";
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request, payload, "application/x-www-form-urlencoded", false);
 
@@ -94,7 +94,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeBundleTasks",
+      assertPayloadEquals(request, "Action=DescribeBundleTasks",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -110,7 +110,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2010-06-15&Action=DescribeBundleTasks&BundleId.1=1&BundleId.2=2",
+      assertPayloadEquals(request, "Action=DescribeBundleTasks&BundleId.1=1&BundleId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeImagesResponseHandlerTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeImagesResponseHandlerTest.java
index 299a694..a827cec 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeImagesResponseHandlerTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/xml/DescribeImagesResponseHandlerTest.java
@@ -92,6 +92,18 @@
 
       assertEquals(result, contents);
    }
+   
+   public void testDiabloWithIncorrectDisplayNameField() {
+      Set<Image> contents = ImmutableSet.of(new Image("us-east-1", Architecture.X86_64, "CentOS 6.2 Server 64-bit 20120125", "", "ami-0000054e",
+               "local (CentOS 6.2 Server 64-bit 20120125)", "", ImageState.AVAILABLE,
+               ImageType.MACHINE, true, Sets.<String> newHashSet(), "aki-0000054c", null, "ari-0000054d",
+               RootDeviceType.INSTANCE_STORE, "/dev/sda1", ImmutableMap.<String, EbsBlockDevice> of(),
+               VirtualizationType.PARAVIRTUAL, Hypervisor.XEN));
+      
+      Set<Image> result = parseImages("/describe_images_nova.xml");
+
+      assertEquals(result.toString(), contents.toString());
+   }
 
    static ParseSax<Set<Image>> createParser() {
       Injector injector = Guice.createInjector(new SaxParserModule(), new AbstractModule() {
diff --git a/apis/ec2/src/test/resources/describe_images_nova.xml b/apis/ec2/src/test/resources/describe_images_nova.xml
new file mode 100644
index 0000000..673d1f4
--- /dev/null
+++ b/apis/ec2/src/test/resources/describe_images_nova.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" ?>
+<DescribeImagesResponse xmlns="http://ec2.amazonaws.com/doc/2009-04-04/">
+    <requestId>f3daebdd-c435-4af4-86fc-d467e4530e57</requestId>
+    <imagesSet>
+        <item>
+            <displayName>CentOS 6.2 Server 64-bit 20120125</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ami-0000054e</imageId>
+            <imageState>available</imageState>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <architecture>x86_64</architecture>
+            <imageLocation>local (CentOS 6.2 Server 64-bit 20120125)</imageLocation>
+            <kernelId>aki-0000054c</kernelId>
+            <ramdiskId>ari-0000054d</ramdiskId>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>machine</imageType>
+        </item>
+    </imagesSet>
+</DescribeImagesResponse>
\ No newline at end of file
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
new file mode 100644
index 0000000..bdc2d7f
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elasticstack;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the ElasticStack API
+ * 
+ * @author Adrian Cole
+ */
+public class ElasticStackApiMetadata extends BaseApiMetadata {
+
+   public ElasticStackApiMetadata() {
+      this(builder()
+            .id("elasticstack")
+            .type(ApiType.COMPUTE)
+            .name("ElasticStack API")
+            .identityName("UUID")
+            .credentialName("Secret API key")
+            .documentation(URI.create("http://www.elasticstack.com/cloud-platform/api")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected ElasticStackApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public ElasticStackApiMetadata build() {
+         return new ElasticStackApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/elasticstack/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/elasticstack/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..562d329
--- /dev/null
+++ b/apis/elasticstack/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.elasticstack.ElasticStackApiMetadata
\ No newline at end of file
diff --git a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiMetadataTest.java
similarity index 69%
rename from providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java
rename to apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiMetadataTest.java
index c0d2996..8d80a68 100644
--- a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.cloudonestorage;
+package org.jclouds.elasticstack;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "cloudonestorage") : providers;
-   }
+@Test(groups = "unit", testName = "ElasticStackApiMetadataTest")
+public class ElasticStackApiMetadataTest extends BaseApiMetadataTest {
 
+   public ElasticStackApiMetadataTest() {
+      super(new ElasticStackApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ProvidersInPropertiesTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ProvidersInPropertiesTest.java
deleted file mode 100644
index 782a057..0000000
--- a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,53 +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.elasticstack;
-
-import org.jclouds.compute.util.ComputeServiceUtils;
-import org.jclouds.rest.Providers;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "elasticstack") : providers;
-      assert Iterables.contains(providers, "elastichosts-lon-p") : providers;
-      assert Iterables.contains(providers, "elastichosts-lon-b") : providers;
-      assert Iterables.contains(providers, "elastichosts-sat-p") : providers;
-   }
-
-   @Test
-   public void testSupportedComputeServiceProviders() {
-      Iterable<String> providers = ComputeServiceUtils.getSupportedProviders();
-      assert Iterables.contains(providers, "elasticstack") : providers;
-      assert Iterables.contains(providers, "elastichosts-lon-p") : providers;
-      assert Iterables.contains(providers, "elastichosts-lon-b") : providers;
-      assert Iterables.contains(providers, "elastichosts-sat-p") : providers;
-   }
-
-}
diff --git a/apis/eucalyptus/src/main/java/org/jclouds/eucalyptus/EucalyptusApiMetadata.java b/apis/eucalyptus/src/main/java/org/jclouds/eucalyptus/EucalyptusApiMetadata.java
new file mode 100644
index 0000000..c8e8877
--- /dev/null
+++ b/apis/eucalyptus/src/main/java/org/jclouds/eucalyptus/EucalyptusApiMetadata.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.eucalyptus;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.ec2.EC2ApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the Eucalyptus (EC2 clone) api.
+ * 
+ * @author Adrian Cole
+ */
+public class EucalyptusApiMetadata extends EC2ApiMetadata {
+
+   public EucalyptusApiMetadata() {
+      this(builder().fromApiMetadata(new EC2ApiMetadata())
+            .id("eucalyptus")
+            .name("Eucalyptus (EC2 clone) API"));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected EucalyptusApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends EC2ApiMetadataBuilder<ConcreteBuilder> {
+
+      @Override
+      public EucalyptusApiMetadata build() {
+         return new EucalyptusApiMetadata(this);
+      }
+   }
+
+   private static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/eucalyptus/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/eucalyptus/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..2475f3f
--- /dev/null
+++ b/apis/eucalyptus/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.eucalyptus.EucalyptusApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/EucalyptusApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/EucalyptusApiMetadataTest.java
index ace0f456..7473d8a 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/eucalyptus/src/test/java/org/jclouds/eucalyptus/EucalyptusApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.eucalyptus;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "EucalyptusApiMetadataTest")
+public class EucalyptusApiMetadataTest extends BaseApiMetadataTest {
 
+   public EucalyptusApiMetadataTest() {
+      super(new EucalyptusApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemApiMetadata.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemApiMetadata.java
new file mode 100644
index 0000000..99d51e6
--- /dev/null
+++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemApiMetadata.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.filesystem;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for jclouds Filesystem-based BlobStore
+ * 
+ * @author Adrian Cole
+ */
+public class FilesystemApiMetadata extends BaseApiMetadata {
+
+   public FilesystemApiMetadata() {
+      this(builder()
+            .id("filesystem")
+            .type(ApiType.BLOBSTORE)
+            .name("Filesystem-based BlobStore")
+            .identityName("Unused")
+            .documentation(URI.create("http://www.jclouds.org/documentation/userguide/blobstore-guide")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected FilesystemApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public FilesystemApiMetadata build() {
+         return new FilesystemApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/apis/filesystem/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/filesystem/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..9219cb2
--- /dev/null
+++ b/apis/filesystem/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.filesystem.FilesystemApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemApiMetadataTest.java
index ace0f456..e5619df 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/filesystem/src/test/java/org/jclouds/filesystem/FilesystemApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.filesystem;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "FilesystemApiMetadataTest")
+public class FilesystemApiMetadataTest extends BaseApiMetadataTest {
 
+   public FilesystemApiMetadataTest() {
+      super(new FilesystemApiMetadata(), ApiType.BLOBSTORE);
+   }
 }
diff --git a/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaApiMetadata.java b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaApiMetadata.java
new file mode 100644
index 0000000..ef27f47
--- /dev/null
+++ b/apis/nova/src/main/java/org/jclouds/openstack/nova/NovaApiMetadata.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.openstack.nova;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for OpenStack Nova Pre-Diablo
+ * 
+ * @author Adrian Cole
+ */
+public class NovaApiMetadata extends BaseApiMetadata {
+
+   public NovaApiMetadata() {
+      this(builder()
+            .id("nova")
+            .type(ApiType.COMPUTE)
+            .name("OpenStack Nova Pre-Diablo API")
+            .identityName("accessKey")
+            .credentialName("secretKey")
+            .documentation(URI.create("http://api.openstack.org/")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected NovaApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public NovaApiMetadata build() {
+         return new NovaApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/nova/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/nova/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..0fa70d2
--- /dev/null
+++ b/apis/nova/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.openstack.nova.NovaApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/nova/src/test/java/org/jclouds/openstack/nova/NovaApiMetadataTest.java
index ace0f456..448a1e0 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/nova/src/test/java/org/jclouds/openstack/nova/NovaApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.openstack.nova;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "NovaApiMetadataTest")
+public class NovaApiMetadataTest extends BaseApiMetadataTest {
 
+   public NovaApiMetadataTest() {
+      super(new NovaApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/openstack-nova-ec2/pom.xml b/apis/openstack-nova-ec2/pom.xml
new file mode 100644
index 0000000..81f3591
--- /dev/null
+++ b/apis/openstack-nova-ec2/pom.xml
@@ -0,0 +1,150 @@
+<?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.api</groupId>
+  <artifactId>openstack-nova-ec2</artifactId>
+  <name>jclouds Eucalyptus api</name>
+  <description>EC2 implementation based on Eucalyptus</description>
+  <packaging>bundle</packaging>
+
+  <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.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-id>
+    <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>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.jclouds.api</groupId>
+      <artifactId>openstack-nova</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <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-core</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.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>
+  </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.openstack-nova-ec2.endpoint>${test.openstack-nova-ec2.endpoint}</test.openstack-nova-ec2.endpoint>
+                    <test.openstack-nova-ec2.api-version>${test.openstack-nova-ec2.api-version}</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>${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>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+  <build>
+    <plugins>
+      <plugin>  
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+            <Export-Package>org.jclouds.openstack.nova.ec2*;version="${project.version}"</Export-Package>
+            <Import-Package>
+              org.jclouds.compute.internal;version="${project.version}",
+              org.jclouds.rest.internal;version="${project.version}",
+              org.jclouds*;version="${project.version}",
+              *
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadata.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadata.java
new file mode 100644
index 0000000..e8ba97c
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadata.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.nova.ec2;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.ec2.EC2ApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Nova's EC2-clone API
+ * 
+ * @author Adrian Cole
+ */
+public class NovaEC2ApiMetadata extends EC2ApiMetadata {
+
+   public NovaEC2ApiMetadata() {
+      this(builder().fromApiMetadata(new EC2ApiMetadata())
+            .id("openstack-nova-ec2")
+            .name("Nova's EC2-clone API"));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected NovaEC2ApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends EC2ApiMetadataBuilder<ConcreteBuilder> {
+
+      @Override
+      public NovaEC2ApiMetadata build() {
+         return new NovaEC2ApiMetadata(this);
+      }
+   }
+
+   private static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ContextBuilder.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ContextBuilder.java
new file mode 100644
index 0000000..7131666
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2ContextBuilder.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.nova.ec2;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.jclouds.ec2.EC2ContextBuilder;
+import org.jclouds.openstack.nova.ec2.config.NovaEC2ComputeServiceContextModule;
+import org.jclouds.openstack.nova.ec2.config.NovaEC2RestClientModule;
+
+import com.google.inject.Module;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class NovaEC2ContextBuilder extends EC2ContextBuilder {
+
+   public NovaEC2ContextBuilder(Properties props) {
+      super(props);
+   }
+
+   
+   @Override
+   protected void addClientModule(List<Module> modules) {
+      modules.add(new NovaEC2RestClientModule());
+   }
+
+   @Override
+   protected void addContextModule(List<Module> modules) {
+      modules.add(new NovaEC2ComputeServiceContextModule());
+   }
+
+}
diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2PropertiesBuilder.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2PropertiesBuilder.java
new file mode 100644
index 0000000..41c4b59
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/NovaEC2PropertiesBuilder.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.openstack.nova.ec2;
+
+import static org.jclouds.Constants.PROPERTY_API_VERSION;
+import static org.jclouds.Constants.PROPERTY_ENDPOINT;
+import static org.jclouds.Constants.PROPERTY_RELAX_HOSTNAME;
+import static org.jclouds.Constants.PROPERTY_TRUST_ALL_CERTS;
+import static org.jclouds.compute.config.ComputeServiceProperties.RESOURCENAME_DELIMITER;
+import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
+import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AUTO_ALLOCATE_ELASTIC_IPS;
+import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
+
+import java.util.Properties;
+
+import org.jclouds.ec2.EC2PropertiesBuilder;
+
+/**
+ * Builds properties used in NovaEC2 Clients
+ * 
+ * @author Adrian Cole
+ */
+public class NovaEC2PropertiesBuilder extends EC2PropertiesBuilder {
+   @Override
+   protected Properties defaultProperties() {
+      Properties properties = super.defaultProperties();
+      properties.setProperty(PROPERTY_API_VERSION, "2009-04-04");
+      properties.setProperty(PROPERTY_ENDPOINT, "http://localhost:8773/services/Cloud");
+      properties.setProperty(PROPERTY_REGIONS, "nova");
+      properties.setProperty(PROPERTY_EC2_AMI_OWNERS, "admin");
+      // hash characters are banned
+      properties.setProperty(RESOURCENAME_DELIMITER, "-");
+      // often, we are dealing with IP addresses, not hostnames
+      properties.setProperty(PROPERTY_RELAX_HOSTNAME, "true");
+      properties.setProperty(PROPERTY_TRUST_ALL_CERTS, "true");
+      properties.setProperty(PROPERTY_EC2_AUTO_ALLOCATE_ELASTIC_IPS, "true");
+      // auth fail sometimes happens in EC2, as the rc.local script that injects the
+      // authorized key executes after ssh has started.  
+      properties.setProperty("jclouds.ssh.max-retries", "7");
+      properties.setProperty("jclouds.ssh.retry-auth", "true");
+      return properties;
+   }
+
+   public NovaEC2PropertiesBuilder(Properties properties) {
+      super(properties);
+   }
+
+}
diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ComputeServiceContextModule.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ComputeServiceContextModule.java
new file mode 100644
index 0000000..5e8b50c
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ComputeServiceContextModule.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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.nova.ec2.config;
+
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.ec2.compute.config.EC2ComputeServiceContextModule;
+import org.jclouds.ec2.compute.loaders.CreateSecurityGroupIfNeeded;
+import org.jclouds.ec2.compute.strategy.ReviseParsedImage;
+import org.jclouds.openstack.nova.ec2.loaders.NovaCreateSecurityGroupIfNeeded;
+import org.jclouds.openstack.nova.ec2.strategy.NovaReviseParsedImage;
+import org.jclouds.openstack.nova.v1_1.compute.functions.ImageToOperatingSystem;
+
+import com.google.common.base.Function;
+import com.google.inject.TypeLiteral;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class NovaEC2ComputeServiceContextModule extends EC2ComputeServiceContextModule {
+
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(new TypeLiteral<Function<org.jclouds.openstack.nova.v1_1.domain.Image, OperatingSystem>>() {
+      }).to(ImageToOperatingSystem.class);
+      bind(ReviseParsedImage.class).to(NovaReviseParsedImage.class);
+      bind(CreateSecurityGroupIfNeeded.class).to(NovaCreateSecurityGroupIfNeeded.class);
+   }
+
+}
diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.java
new file mode 100644
index 0000000..0ac573f
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2RestClientModule.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.openstack.nova.ec2.config;
+
+import org.jclouds.ec2.EC2AsyncClient;
+import org.jclouds.ec2.EC2Client;
+import org.jclouds.ec2.config.EC2RestClientModule;
+import org.jclouds.ec2.suppliers.DescribeAvailabilityZonesInRegion;
+import org.jclouds.http.RequiresHttp;
+import org.jclouds.location.config.LocationModule;
+import org.jclouds.location.suppliers.RegionIdToZoneIdsSupplier;
+import org.jclouds.location.suppliers.ZoneIdsSupplier;
+import org.jclouds.location.suppliers.derived.ZoneIdsFromRegionIdToZoneIdsValues;
+import org.jclouds.rest.ConfiguresRestClient;
+
+import com.google.inject.Scopes;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@RequiresHttp
+@ConfiguresRestClient
+public class NovaEC2RestClientModule extends EC2RestClientModule<EC2Client, EC2AsyncClient> {
+
+   public NovaEC2RestClientModule() {
+      super(EC2Client.class, EC2AsyncClient.class, DELEGATE_MAP);
+   }
+
+   @Override
+   protected void installLocations() {
+      install(new LocationModule());
+      bind(RegionIdToZoneIdsSupplier.class).to(DescribeAvailabilityZonesInRegion.class).in(Scopes.SINGLETON);
+      // there is only one region, and its endpoint is the same as the provider
+      bind(ZoneIdsSupplier.class).to(ZoneIdsFromRegionIdToZoneIdsValues.class).in(Scopes.SINGLETON);
+   }
+}
diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/loaders/NovaCreateSecurityGroupIfNeeded.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/loaders/NovaCreateSecurityGroupIfNeeded.java
new file mode 100644
index 0000000..c702534
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/loaders/NovaCreateSecurityGroupIfNeeded.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.nova.ec2.loaders;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.aws.AWSResponseException;
+import org.jclouds.ec2.EC2Client;
+import org.jclouds.ec2.compute.domain.RegionAndName;
+import org.jclouds.ec2.compute.loaders.CreateSecurityGroupIfNeeded;
+
+import com.google.common.base.Predicate;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaCreateSecurityGroupIfNeeded extends CreateSecurityGroupIfNeeded {
+
+   @Inject
+   public NovaCreateSecurityGroupIfNeeded(EC2Client ec2Client,
+            @Named("SECURITY") Predicate<RegionAndName> securityGroupEventualConsistencyDelay) {
+      super(checkNotNull(ec2Client, "ec2Client").getSecurityGroupServices(), securityGroupEventualConsistencyDelay);
+   }
+
+   protected void authorizeGroupToItself(String region, String name) {
+      try {
+         super.authorizeGroupToItself(region, name);
+      } catch (AWSResponseException e) {
+         logger.warn(e, "<< error authorizing securityGroup(%s)", name);
+      }
+   }
+
+}
diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/strategy/NovaReviseParsedImage.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/strategy/NovaReviseParsedImage.java
new file mode 100644
index 0000000..2766523
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/strategy/NovaReviseParsedImage.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.nova.ec2.strategy;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.inject.Inject;
+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.ec2.compute.strategy.ReviseParsedImage;
+import org.jclouds.openstack.nova.v1_1.domain.Image;
+
+import com.google.common.base.Function;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class NovaReviseParsedImage implements ReviseParsedImage {
+
+   private final Function<Image, OperatingSystem> imageToOs;
+
+   @Inject
+   public NovaReviseParsedImage(Function<Image, OperatingSystem> imageToOs) {
+      this.imageToOs = checkNotNull(imageToOs, "imageToOs");
+   }
+
+   @Override
+   public void reviseParsedImage(org.jclouds.ec2.domain.Image from, ImageBuilder builder, OsFamily family,
+            OperatingSystem.Builder osBuilder) {
+      Image image = Image.builder().id(from.getId()).name(from.getName()).build();
+      OperatingSystem os = imageToOs.apply(image);
+      osBuilder.description(os.getDescription());
+      osBuilder.family(os.getFamily());
+      osBuilder.name(os.getName());
+      osBuilder.is64Bit(os.is64Bit());
+      osBuilder.version(os.getVersion());
+      // arch is accurate already
+   }
+}
\ No newline at end of file
diff --git a/apis/openstack-nova-ec2/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/openstack-nova-ec2/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..f77b757
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.openstack.nova.ec2.NovaEC2ApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadataTest.java
index ace0f456..7d63c3a 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/NovaEC2ApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.openstack.nova.ec2;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "NovaEC2ApiMetadataTest")
+public class NovaEC2ApiMetadataTest extends BaseApiMetadataTest {
 
+   public NovaEC2ApiMetadataTest() {
+      super(new NovaEC2ApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/compute/NovaEC2ComputeServiceLiveTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/compute/NovaEC2ComputeServiceLiveTest.java
new file mode 100644
index 0000000..14d614e
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/compute/NovaEC2ComputeServiceLiveTest.java
@@ -0,0 +1,41 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.nova.ec2.compute;
+
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.ec2.compute.EC2ComputeServiceLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "NovaEC2ComputeServiceLiveTest")
+public class NovaEC2ComputeServiceLiveTest extends EC2ComputeServiceLiveTest {
+
+   public NovaEC2ComputeServiceLiveTest() {
+      provider = "openstack-nova-ec2";
+   }
+   
+   protected void checkResponseEqualsHostname(ExecResponse execResponse, NodeMetadata node1) {
+      // hostname is not predictable based on node metadata
+      assert execResponse.getOutput().trim().equals("ubuntu");
+   }
+}
diff --git a/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/strategy/NovaReviseParsedImageTest.java b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/strategy/NovaReviseParsedImageTest.java
new file mode 100644
index 0000000..d5b7995
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/test/java/org/jclouds/openstack/nova/ec2/strategy/NovaReviseParsedImageTest.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.nova.ec2.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.functions.EC2ImageParser;
+import org.jclouds.ec2.compute.strategy.EC2PopulateDefaultLoginCredentialsForImageStrategy;
+import org.jclouds.ec2.domain.Image;
+import org.jclouds.ec2.xml.DescribeImagesResponseHandlerTest;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.openstack.nova.v1_1.compute.functions.ImageToOperatingSystem;
+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 = "NovaReviseParsedImageTest")
+public class NovaReviseParsedImageTest {
+
+   public void test() {
+
+      Set<org.jclouds.compute.domain.Image> result = convertImages("/nova_ec2_images.xml");
+      assertEquals(result.size(), 7);
+
+      assertEquals(
+            Iterables.get(result, 4).toString(),
+            new ImageBuilder()
+                  .operatingSystem(
+                        OperatingSystem.builder().family(OsFamily.UBUNTU).arch("paravirtual").version("10.10")
+                              .name("Ubuntu Maverick 10.10 Server 64-bit 20111212")
+                              .description("Ubuntu Maverick 10.10 Server 64-bit 20111212").is64Bit(true)
+                              .build())
+                  .name("Ubuntu Maverick 10.10 Server 64-bit 20111212")
+                  .description("")
+                  .defaultCredentials(new LoginCredentials("root", false))
+                  .id("us-east-1/ami-000004d6")
+                  .providerId("ami-000004d6")
+                  .location(defaultLocation)
+                  .userMetadata(
+                        ImmutableMap.of("owner", "", "rootDeviceType", "instance-store", "virtualizationType",
+                              "paravirtual", "hypervisor", "xen")).build().toString());
+   }
+
+   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));
+
+      Set<Image> result = DescribeImagesResponseHandlerTest.parseImages(resource);
+      EC2ImageParser parser = new EC2ImageParser(new EC2PopulateDefaultLoginCredentialsForImageStrategy(), map,
+               Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet.<Location> of(defaultLocation)), Suppliers
+                        .ofInstance(defaultLocation), new NovaReviseParsedImage(new ImageToOperatingSystem(map)));
+      return Sets.newLinkedHashSet(Iterables.filter(Iterables.transform(result, parser), Predicates.notNull()));
+   }
+
+}
diff --git a/apis/openstack-nova-ec2/src/test/resources/nova_ec2_images.xml b/apis/openstack-nova-ec2/src/test/resources/nova_ec2_images.xml
new file mode 100644
index 0000000..6d2dc08
--- /dev/null
+++ b/apis/openstack-nova-ec2/src/test/resources/nova_ec2_images.xml
@@ -0,0 +1,259 @@
+<?xml version="1.0" ?>
+<DescribeImagesResponse xmlns="http://ec2.amazonaws.com/doc/2009-04-04/">
+    <requestId>b5ad23de-f77d-
+        4b66-bfc2-ab01ceccb97b</requestId>
+    <imagesSet>
+        <item>
+            <displayName>Debian Squeeze 6.0.3 Server 64-bit 20120123</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ami-00000551</imageId>
+            <imageState>available</imageState>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Debian Squeeze 6.0.3 Server 64-bit
+                20120123)</imageLocation>
+            <kernelId>aki-0000054f</kernelId>
+            <ramdiskId>ari-00000550</ramdiskId>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>machine</imageType>
+        </item>
+        <item>
+            <displayName>Debian Squeeze 6.0.3 Server 64-bit 20120123
+                (Ramdisk)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ari-00000550</imageId>
+            <imageState>available</imageState>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Debian Squeeze 6.0.3 Server 64-bit
+                20120123 (Ramdisk))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>ramdisk</imageType>
+        </item>
+        <item>
+            <displayName>Debian Squeeze 6.0.3 Server 64-bit 20120123
+                (Kernel)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>aki-0000054f</imageId>
+            <imageState>available</imageState>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Debian Squeeze 6.0.3 Server 64-bit
+                20120123 (Kernel))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>kernel</imageType>
+        </item>
+        <item>
+            <displayName>CentOS 6.2 Server 64-bit 20120125</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ami-0000054e</imageId>
+            <imageState>available</imageState>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <architecture>x86_64</architecture>
+            <imageLocation>local (CentOS 6.2 Server 64-bit 20120125)</imageLocation>
+            <kernelId>aki-0000054c</kernelId>
+            <ramdiskId>ari-0000054d</ramdiskId>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>machine</imageType>
+        </item>
+        <item>
+            <displayName>CentOS 6.2 Server 64-bit 20120125 (Ramdisk)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ari-0000054d</imageId>
+            <imageState>available</imageState>
+            <architecture>x86_64</architecture>
+            <imageLocation>local (CentOS 6.2 Server 64-bit 20120125
+                (Ramdisk))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>ramdisk</imageType>
+        </item>
+        <item>
+            <displayName>CentOS 6.2 Server 64-bit 20120125 (Kernel)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>aki-0000054c</imageId>
+            <imageState>available</imageState>
+            <architecture>x86_64</architecture>
+            <imageLocation>local (CentOS 6.2 Server 64-bit 20120125
+                (Kernel))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>kernel</imageType>
+        </item>
+        <item>
+            <displayName>Ubuntu Oneiric 11.10 Server 64-bit 20111212</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ami-000004da</imageId>
+            <imageState>available</imageState>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Ubuntu Oneiric 11.10 Server 64-bit
+                20111212)</imageLocation>
+            <kernelId>aki-000004d9</kernelId>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>machine</imageType>
+        </item>
+        <item>
+            <displayName>Ubuntu Oneiric 11.10 Server 64-bit 20111212
+                (Kernel)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>aki-000004d9</imageId>
+            <imageState>available</imageState>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Ubuntu Oneiric 11.10 Server 64-bit
+                20111212 (Kernel))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>kernel</imageType>
+        </item>
+        <item>
+            <displayName>Ubuntu Natty 11.04 Server 64-bit 20111212</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ami-000004d8</imageId>
+            <imageState>available</imageState>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Ubuntu Natty 11.04 Server 64-bit
+                20111212)</imageLocation>
+            <kernelId>aki-000004d7</kernelId>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>machine</imageType>
+        </item>
+        <item>
+            <displayName>Ubuntu Natty 11.04 Server 64-bit 20111212
+                (Kernel)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>aki-000004d7</imageId>
+            <imageState>available</imageState>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Ubuntu Natty 11.04 Server 64-bit
+                20111212 (Kernel))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>kernel</imageType>
+        </item>
+        <item>
+            <displayName>Ubuntu Maverick 10.10 Server 64-bit 20111212</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ami-000004d6</imageId>
+            <imageState>available</imageState>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Ubuntu Maverick 10.10 Server 64-bit
+                20111212)</imageLocation>
+            <kernelId>aki-000004d5</kernelId>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>machine</imageType>
+        </item>
+        <item>
+            <displayName>Ubuntu Maverick 10.10 Server 64-bit 20111212
+                (Kernel)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>aki-000004d5</imageId>
+            <imageState>available</imageState>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Ubuntu Maverick 10.10 Server 64-bit
+                20111212 (Kernel))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>kernel</imageType>
+        </item>
+        <item>
+            <displayName>Ubuntu Lucid 10.04 LTS Server 64-bit 20111212</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ami-000004d4</imageId>
+            <imageState>available</imageState>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Ubuntu Lucid 10.04 LTS Server 64-bit
+                20111212)</imageLocation>
+            <kernelId>aki-000004d3</kernelId>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>machine</imageType>
+        </item>
+        <item>
+            <displayName>Ubuntu Lucid 10.04 LTS Server 64-bit 20111212
+                (Kernel)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>aki-000004d3</imageId>
+            <imageState>available</imageState>
+            <architecture>amd64</architecture>
+            <imageLocation>local (Ubuntu Lucid 10.04 LTS Server 64-bit
+                20111212 (Kernel))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>kernel</imageType>
+        </item>
+        <item>
+            <displayName>CentOS 5.6 Server 64-bit 20111207</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ami-000004d2</imageId>
+            <imageState>available</imageState>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <architecture>x86_64</architecture>
+            <imageLocation>local (CentOS 5.6 Server 64-bit 20111207)</imageLocation>
+            <kernelId>aki-000004d0</kernelId>
+            <ramdiskId>ari-000004d1</ramdiskId>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>machine</imageType>
+        </item>
+        <item>
+            <displayName>CentOS 5.6 Server 64-bit 20111207 (Ramdisk)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>ari-000004d1</imageId>
+            <imageState>available</imageState>
+            <architecture>x86_64</architecture>
+            <imageLocation>local (CentOS 5.6 Server 64-bit 20111207
+                (Ramdisk))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>ramdisk</imageType>
+        </item>
+        <item>
+            <displayName>CentOS 5.6 Server 64-bit 20111207 (Kernel)</displayName>
+            <description />
+            <imageOwnerId />
+            <isPublic>true</isPublic>
+            <imageId>aki-000004d0</imageId>
+            <imageState>available</imageState>
+            <architecture>x86_64</architecture>
+            <imageLocation>local (CentOS 5.6 Server 64-bit 20111207
+                (Kernel))</imageLocation>
+            <rootDeviceType>instance-store</rootDeviceType>
+            <rootDeviceName>/dev/sda1</rootDeviceName>
+            <imageType>kernel</imageType>
+        </item>
+    </imagesSet>
+</DescribeImagesResponse>
\ No newline at end of file
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadata.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadata.java
new file mode 100644
index 0000000..3df8fa8
--- /dev/null
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadata.java
@@ -0,0 +1,58 @@
+package org.jclouds.openstack.nova.v1_1;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for OpenStack Nova Diablo+
+ * 
+ * @author Adrian Cole
+ */
+public class NovaApiMetadata extends BaseApiMetadata {
+
+   public NovaApiMetadata() {
+      this(builder()
+            .id("openstack-nova")
+            .type(ApiType.COMPUTE)
+            .name("OpenStack Nova Diablo+ API")
+            .identityName("tenantId:user")
+            .credentialName("password")
+            .documentation(URI.create("http://api.openstack.org/")));
+   }
+
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected NovaApiMetadata(NovaApiMetadataBuilder<?> builder) {
+      super(builder);
+   }
+
+   public static class NovaApiMetadataBuilder<B extends NovaApiMetadataBuilder<B>> extends Builder<B> {
+
+      @Override
+      public NovaApiMetadata build() {
+         return new NovaApiMetadata(this);
+      }
+   }
+
+   private static class NovaConcreteBuilder extends NovaApiMetadataBuilder<NovaConcreteBuilder> {
+
+      @Override
+      public NovaApiMetadata build() {
+         return new NovaApiMetadata(this);
+      }
+   }
+
+   private static NovaConcreteBuilder builder() {
+      return new NovaConcreteBuilder();
+   }
+
+   @Override
+   public NovaApiMetadataBuilder<?> toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/ImageToOperatingSystem.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/ImageToOperatingSystem.java
index 7a81a06..cac8341 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/ImageToOperatingSystem.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/compute/functions/ImageToOperatingSystem.java
@@ -106,7 +106,7 @@
                   osFamily = OsFamily.UBUNTU;
                   osVersion = ubuntuVersion;
                } else {
-                  logger.trace("could not parse operating system family for image(%s): %s", imageNameParts);
+                  logger.trace("could not parse operating system family for image(%s): %s", from.getId(), imageNameParts);
                   osFamily = OsFamily.UNRECOGNIZED;
                }
             }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java
index 9b61496..718ad18 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java
@@ -104,7 +104,7 @@
    @DELETE
    @Path("/os-security-groups/{id}")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   @Consumes
+   @Consumes(MediaType.APPLICATION_JSON)
    ListenableFuture<Boolean> deleteSecurityGroup(@PathParam("id") String id);
 
    /**
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerAsyncClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerAsyncClient.java
index 994dd29..2640f74 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerAsyncClient.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerAsyncClient.java
@@ -34,6 +34,7 @@
 import org.jclouds.openstack.filters.AuthenticateRequest;
 import org.jclouds.openstack.nova.v1_1.domain.RebootType;
 import org.jclouds.openstack.nova.v1_1.domain.Server;
+import org.jclouds.openstack.nova.v1_1.functions.ParseImageIdFromLocationHeader;
 import org.jclouds.openstack.nova.v1_1.options.CreateServerOptions;
 import org.jclouds.openstack.nova.v1_1.options.RebuildServerOptions;
 import org.jclouds.rest.annotations.ExceptionParser;
@@ -41,9 +42,11 @@
 import org.jclouds.rest.annotations.Payload;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SelectJson;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Unwrap;
+import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
 import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
 import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
@@ -182,4 +185,17 @@
    @Produces(MediaType.APPLICATION_JSON)
    @Payload("%7B\"server\":%7B\"name\":\"{name}\"%7D%7D")
    ListenableFuture<Void> renameServer(@PathParam("id") String id, @PayloadParam("name") String newName);
+
+   /**
+    * @see ServerClient#createImageFromServer
+    */
+   @POST
+   @Path("/servers/{id}/action")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @Produces(MediaType.APPLICATION_JSON)
+   @Payload("%7B\"createImage\":%7B\"name\":\"{name}\", \"metadata\": %7B%7D%7D%7D")
+   @ExceptionParser(MapHttp4xxCodesToExceptions.class)
+   @ResponseParser(ParseImageIdFromLocationHeader.class)
+   ListenableFuture<String> createImageFromServer(@PayloadParam("name") String name, @PathParam("id") String id);
+
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerClient.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerClient.java
index b0723f1..51003d3 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerClient.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/features/ServerClient.java
@@ -156,4 +156,17 @@
     *           The new name for the server
     */
    void renameServer(String id, String newName);
+
+   /**
+    * Create an image from a server.
+    *
+    * @param name
+    *           The name of the new image
+    * @param id
+    *           id of the server
+    *
+    * @return ID of the new / updated image
+    */
+   String createImageFromServer(String name, String id);
+
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/ParseImageIdFromLocationHeader.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/ParseImageIdFromLocationHeader.java
new file mode 100644
index 0000000..1bdeb2c
--- /dev/null
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/functions/ParseImageIdFromLocationHeader.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.nova.v1_1.functions;
+
+
+import javax.inject.Singleton;
+
+import javax.ws.rs.core.HttpHeaders;
+
+import org.jclouds.openstack.nova.v1_1.domain.Image;
+import org.jclouds.http.HttpResponse;
+
+import com.google.common.base.Function;
+ 
+
+/**
+ * This parses {@link Image} from the body of the link in the Location header of the HTTPResponse.
+ * 
+ * @author Tim Miller
+ */
+@Singleton
+public class ParseImageIdFromLocationHeader implements Function<HttpResponse, String> {
+
+	public String apply(HttpResponse response) {
+        String location = response.getFirstHeaderOrNull(HttpHeaders.LOCATION);
+        String[] parts = location.split("/");
+        return parts[parts.length - 1];
+    }
+}
diff --git a/apis/openstack-nova/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/openstack-nova/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..31eaaf8
--- /dev/null
+++ b/apis/openstack-nova/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.openstack.nova.v1_1.NovaApiMetadata
\ No newline at end of file
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadataTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadataTest.java
new file mode 100644
index 0000000..2057e42
--- /dev/null
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/NovaApiMetadataTest.java
@@ -0,0 +1,17 @@
+package org.jclouds.openstack.nova.v1_1;
+
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "NovaApiMetadataTest")
+public class NovaApiMetadataTest extends BaseApiMetadataTest {
+
+   public NovaApiMetadataTest() {
+      super(new NovaApiMetadata(), ApiType.COMPUTE);
+   }
+}
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java
index 1596e98..a8baa07 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java
@@ -142,7 +142,7 @@
       HttpRequest deleteSecurityGroup = HttpRequest.builder().method("DELETE").endpoint(
                URI.create("https://compute.north.host/v1.1/3456/os-security-groups/160"))
                .headers(
-                        ImmutableMultimap.<String, String> builder().put("Accept", "*/*")
+                        ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
                                  .put("X-Auth-Token", authToken).build()).build();
 
       HttpResponse deleteSecurityGroupResponse = HttpResponse.builder().statusCode(202).build();
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java
index c59690c..35de130 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java
@@ -20,6 +20,7 @@
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import java.net.URI;
 
@@ -37,7 +38,7 @@
 
 /**
  * Tests annotation parsing of {@code ServerAsyncClient}
- * 
+ *
  * @author Adrian Cole
  */
 @Test(groups = "unit", testName = "ServerAsyncClientTest")
@@ -80,7 +81,7 @@
 
       assertTrue(clientWhenNoServersExist.getServerClientForZone("az-1.region-a.geo-1").listServers().isEmpty());
    }
-   
+
    public void testCreateServerWhenResponseIs202() throws Exception {
       HttpRequest createServer = HttpRequest
             .builder()
@@ -93,7 +94,7 @@
                      "{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\"}}","application/json"))
             .build();
 
-     
+
       HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
             .payload(payloadFromResourceWithContentType("/new_server.json","application/json; charset=UTF-8")).build();
 
@@ -117,7 +118,7 @@
                   "{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"security_groups\":[{\"name\":\"group2\"},{\"name\":\"group1\"}]}}","application/json"))
          .build();
 
-  
+
       HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
          .payload(payloadFromResourceWithContentType("/new_server.json","application/json; charset=UTF-8")).build();
 
@@ -130,4 +131,61 @@
               new ParseCreatedServerTest().expected().toString());
    }
 
+   public void testCreateImageWhenResponseIs2xx() throws Exception {
+	   String serverId = "123";
+	   String imageId = "456";
+	   String imageName = "foo";
+
+	   HttpRequest createImage = HttpRequest
+			   .builder()
+			   .method("POST")
+			   .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/" + serverId + "/action"))
+			   .headers(
+					   ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
+					   .put("X-Auth-Token", authToken).build())
+			   .payload(payloadFromStringWithContentType(
+					   "{\"createImage\":{\"name\":\"" + imageName + "\", \"metadata\": {}}}", "application/json"))
+               .build();
+
+	   HttpResponse createImageResponse = HttpResponse.builder()
+			   .statusCode(200)
+			   .headers(
+					   ImmutableMultimap.<String, String> builder()
+					   .put("Location", "https://compute.north.host/v1.1/3456/images/" + imageId).build()).build();
+
+	   NovaClient clientWhenServerExists = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
+	            responseWithKeystoneAccess, createImage, createImageResponse);
+
+	   assertEquals(clientWhenServerExists.getServerClientForZone("az-1.region-a.geo-1").createImageFromServer(imageName, serverId),
+			   imageId);
+   }
+
+   public void testCreateImageWhenResponseIs404IsEmpty() throws Exception {
+	   String serverId = "123";
+	   String imageName = "foo";
+
+	   HttpRequest createImage = HttpRequest
+			   .builder()
+			   .method("POST")
+			   .endpoint(URI.create("https://compute.north.host/v1.1/3456/servers/" + serverId + "/action"))
+			   .headers(
+					   ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
+					   .put("X-Auth-Token", authToken)
+					   .put("Content-Type", "application/json").build())
+			   .payload(payloadFromStringWithContentType(
+					   "{\"createImage\":{\"name\":\"" + imageName + "\", \"metadata\": {}}}", "application/json"))
+               .build();
+
+	   HttpResponse createImageResponse = HttpResponse.builder().statusCode(404).build();
+	   NovaClient clientWhenServerDoesNotExist = requestsSendResponses(keystoneAuthWithUsernameAndPassword,
+	            responseWithKeystoneAccess, createImage, createImageResponse);
+
+	   try {
+		   clientWhenServerDoesNotExist.getServerClientForZone("az-1.region-a.geo-1").createImageFromServer(imageName, serverId);
+		   fail("Expected an exception.");
+	   } catch (Exception e) {
+		   ;
+	   }
+   }
+
 }
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java
new file mode 100644
index 0000000..d4b973b
--- /dev/null
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java
@@ -0,0 +1,130 @@
+package org.jclouds.openstack.nova.v1_1.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.Set;
+
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.internal.ClassMethodArgsAndReturnVal;
+import org.jclouds.openstack.nova.v1_1.domain.Extension;
+import org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces;
+import org.jclouds.openstack.nova.v1_1.extensions.KeyPairAsyncClient;
+import org.jclouds.openstack.services.ServiceType;
+import org.jclouds.rest.annotations.Delegate;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Functions;
+import com.google.common.base.Optional;
+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.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest")
+public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest {
+
+   Extension keypairs = Extension.builder().alias("os-keypairs").name("Keypairs").namespace(
+            URI.create("http://docs.openstack.org/ext/keypairs/api/v1.1")).updated(
+            new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-08-08T00:00:00+00:00")).description(
+            "Keypair Support").build();
+
+   @org.jclouds.openstack.services.Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.KEYPAIRS)
+   static interface KeyPairIPAsyncClient {
+
+   }
+
+   Extension floatingIps = Extension.builder().alias("os-floating-ips").name("Floating_ips").namespace(
+            URI.create("http://docs.openstack.org/ext/floating_ips/api/v1.1")).updated(
+            new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-06-16T00:00:00+00:00")).description(
+            "Floating IPs support").build();
+
+   @org.jclouds.openstack.services.Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.FLOATING_IPS)
+   static interface FloatingIPAsyncClient {
+
+   }
+
+   static interface NovaAsyncClient {
+
+      @Delegate
+      Optional<FloatingIPAsyncClient> getFloatingIPExtensionForZone(String zone);
+
+      @Delegate
+      Optional<KeyPairAsyncClient> getKeyPairExtensionForZone(String zone);
+
+   }
+
+   ClassMethodArgsAndReturnVal getFloatingIPExtension() throws SecurityException, NoSuchMethodException {
+      return ClassMethodArgsAndReturnVal.builder().clazz(FloatingIPAsyncClient.class).method(
+               NovaAsyncClient.class.getDeclaredMethod("getFloatingIPExtensionForZone", String.class)).args(
+               new Object[] { "expectedzone" }).returnVal("foo").build();
+   }
+
+   ClassMethodArgsAndReturnVal getKeyPairExtension() throws SecurityException, NoSuchMethodException {
+      return ClassMethodArgsAndReturnVal.builder().clazz(KeyPairAsyncClient.class).method(
+               NovaAsyncClient.class.getDeclaredMethod("getKeyPairExtensionForZone", String.class)).args(
+               new Object[] { "expectedzone" }).returnVal("foo").build();
+   }
+
+   public void testPresentWhenExtensionsIncludeNamespaceFromAnnotationAbsentWhenNot() throws SecurityException, NoSuchMethodException {
+
+      assertEquals(whenExtensionsInclude(keypairs, floatingIps).apply(getFloatingIPExtension()), Optional.of("foo"));
+      assertEquals(whenExtensionsInclude(keypairs, floatingIps).apply(getKeyPairExtension()), Optional.of("foo"));
+      assertEquals(whenExtensionsInclude(keypairs).apply(getFloatingIPExtension()), Optional.absent());
+      assertEquals(whenExtensionsInclude(floatingIps).apply(getKeyPairExtension()), Optional.absent());
+   }
+   
+   public void testZoneWithoutExtensionsReturnsAbsent() throws SecurityException, NoSuchMethodException {
+      assertEquals(whenExtensionsInclude(floatingIps).apply(
+               getFloatingIPExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent());
+      assertEquals(whenExtensionsInclude(keypairs).apply(
+               getKeyPairExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent());
+   }
+
+   /**
+    * It is possible that the /extensions call returned the correct extension, but that the
+    * namespaces were different, for whatever reason. One way to address this is to have a multimap
+    * of the authoritative namespace to alternate onces, which could be wired up with guice
+    * 
+    */
+   @Test(enabled = false)
+   public void testPresentWhenAliasForExtensionMapsToNamespace() throws SecurityException, NoSuchMethodException {
+      Extension keypairsWithDifferentNamespace = keypairs.toBuilder().namespace(
+               URI.create("http://docs.openstack.org/ext/arbitrarilydifferent/keypairs/api/v1.1")).build();
+
+      Multimap<URI, URI> aliases = ImmutableMultimap.of(keypairs.getNamespace(), keypairsWithDifferentNamespace
+               .getNamespace());
+
+      assertEquals(whenExtensionsAndAliasesInclude(ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply(
+               getFloatingIPExtension()), Optional.of("foo"));
+
+   }
+
+   //
+
+   private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsInclude(
+            Extension... extensions) {
+      return whenExtensionsAndAliasesInclude(ImmutableSet.copyOf(extensions), ImmutableMultimap.<URI, URI> of());
+   }
+
+   private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsAndAliasesInclude(
+            Set<Extension> extensions, Multimap<URI, URI> aliases) {
+      LoadingCache<String, Set<Extension>> extensionsForZone = CacheBuilder.newBuilder().build(
+               CacheLoader.from(Functions.forMap(ImmutableMap.of("expectedzone", extensions, "differentzone",
+                        ImmutableSet.<Extension> of()))));
+
+      PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet fn = new PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet(
+               extensionsForZone);
+      // TODO: change the constructor to accept aliases, or add an @Inject(optional=true) field inside the class
+      // PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet fn = new
+      // PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet(
+      // extensionsForZone, aliases);
+      return fn;
+   }
+}
diff --git a/apis/pom.xml b/apis/pom.xml
index 448260d..b45bd1a 100644
--- a/apis/pom.xml
+++ b/apis/pom.xml
@@ -48,6 +48,7 @@
        <module>atmos</module>
        <module>nova</module>
        <module>openstack-nova</module>
+       <module>openstack-nova-ec2</module>
        <module>cloudwatch</module>
        <module>cloudsigma</module>
        <module>cloudstack</module>
diff --git a/apis/s3/src/main/java/org/jclouds/s3/S3ApiMetadata.java b/apis/s3/src/main/java/org/jclouds/s3/S3ApiMetadata.java
new file mode 100644
index 0000000..d6a3014
--- /dev/null
+++ b/apis/s3/src/main/java/org/jclouds/s3/S3ApiMetadata.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.s3;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Amazon's S3 api.
+ * 
+ * @author Adrian Cole
+ */
+public class S3ApiMetadata extends BaseApiMetadata {
+
+   public S3ApiMetadata() {
+      this(builder()
+            .id("s3")
+            .type(ApiType.BLOBSTORE)
+            .name("Amazon Simple Storage Service (S3) API")
+            .identityName("Access Key ID")
+            .credentialName("Secret Access Key")
+            .documentation(URI.create("http://docs.amazonwebservices.com/AmazonS3/latest/API")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected S3ApiMetadata(S3ApiMetadataBuilder<?> builder) {
+      super(builder);
+   }
+   
+   public static class S3ApiMetadataBuilder<B extends S3ApiMetadataBuilder<B>> extends Builder<B> {
+
+      @Override
+      public S3ApiMetadata build() {
+         return new S3ApiMetadata(this);
+      }
+   }
+
+   private static class S3ConcreteBuilder extends S3ApiMetadataBuilder<S3ConcreteBuilder> {
+
+      @Override
+      public S3ApiMetadata build() {
+         return new S3ApiMetadata(this);
+      }
+   }
+
+   private static S3ConcreteBuilder builder() {
+      return new S3ConcreteBuilder();
+   }
+
+   @Override
+   public S3ApiMetadataBuilder<?> toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/s3/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/s3/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..e206c63
--- /dev/null
+++ b/apis/s3/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.s3.S3ApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/apis/s3/src/test/java/org/jclouds/s3/S3ApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to apis/s3/src/test/java/org/jclouds/s3/S3ApiMetadataTest.java
index ace0f456..b18deec 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/S3ApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.s3;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "S3ApiMetadataTest")
+public class S3ApiMetadataTest extends BaseApiMetadataTest {
 
+   public S3ApiMetadataTest() {
+      super(new S3ApiMetadata(), ApiType.BLOBSTORE);
+   }
 }
diff --git a/apis/swift/src/main/java/org/jclouds/openstack/swift/SwiftApiMetadata.java b/apis/swift/src/main/java/org/jclouds/openstack/swift/SwiftApiMetadata.java
new file mode 100644
index 0000000..665b84e
--- /dev/null
+++ b/apis/swift/src/main/java/org/jclouds/openstack/swift/SwiftApiMetadata.java
@@ -0,0 +1,49 @@
+package org.jclouds.openstack.swift;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for OpenStack Swift Pre-Diablo
+ * 
+ * @author Adrian Cole
+ */
+public class SwiftApiMetadata extends BaseApiMetadata {
+
+   public SwiftApiMetadata() {
+      this(builder()
+            .id("swift")
+            .type(ApiType.BLOBSTORE)
+            .name("OpenStack Swift Pre-Diablo API")
+            .identityName("tenantId:user")
+            .credentialName("password")
+            .documentation(URI.create("http://api.openstack.org/")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SwiftApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SwiftApiMetadata build() {
+         return new SwiftApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/swift/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/swift/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..884acc5
--- /dev/null
+++ b/apis/swift/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.openstack.swift.SwiftApiMetadata
\ No newline at end of file
diff --git a/apis/swift/src/test/java/org/jclouds/openstack/swift/SwiftApiMetadataTest.java b/apis/swift/src/test/java/org/jclouds/openstack/swift/SwiftApiMetadataTest.java
new file mode 100644
index 0000000..299f16e
--- /dev/null
+++ b/apis/swift/src/test/java/org/jclouds/openstack/swift/SwiftApiMetadataTest.java
@@ -0,0 +1,17 @@
+package org.jclouds.openstack.swift;
+
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "SwiftApiMetadataTest")
+public class SwiftApiMetadataTest extends BaseApiMetadataTest {
+
+   public SwiftApiMetadataTest() {
+      super(new SwiftApiMetadata(), ApiType.BLOBSTORE);
+   }
+}
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudApiMetadata.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudApiMetadata.java
new file mode 100644
index 0000000..8b00adb
--- /dev/null
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudApiMetadata.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.vcloud;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for VCloud 1.0 API
+ * 
+ * @author Adrian Cole
+ */
+public class VCloudApiMetadata extends BaseApiMetadata {
+
+   public VCloudApiMetadata() {
+      this(builder()
+            .id("vcloud")
+            .type(ApiType.COMPUTE)
+            .name("VCloud 1.0 API")
+            .identityName("User at Organization (user@org)")
+            .credentialName("Password")
+            .documentation(URI.create("http://www.vmware.com/support/pubs/vcd_pubs.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected VCloudApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public VCloudApiMetadata build() {
+         return new VCloudApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/vcloud/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/vcloud/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..d800a94
--- /dev/null
+++ b/apis/vcloud/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.vcloud.VCloudApiMetadata
\ No newline at end of file
diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/ProvidersInPropertiesTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/ProvidersInPropertiesTest.java
deleted file mode 100644
index 771a700..0000000
--- a/apis/vcloud/src/test/java/org/jclouds/vcloud/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,47 +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.vcloud;
-
-import org.jclouds.compute.util.ComputeServiceUtils;
-import org.jclouds.rest.Providers;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "vcloud") : providers;
-   }
-
-   @Test
-   public void testSupportedComputeServiceProviders() {
-      Iterable<String> providers = ComputeServiceUtils.getSupportedProviders();
-      assert Iterables.contains(providers, "vcloud") : providers;
-   }
-
-}
diff --git a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudApiMetadataTest.java
similarity index 69%
copy from providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java
copy to apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudApiMetadataTest.java
index c0d2996..3384949 100644
--- a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java
+++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.cloudonestorage;
+package org.jclouds.vcloud;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "cloudonestorage") : providers;
-   }
+@Test(groups = "unit", testName = "VCloudApiMetadataTest")
+public class VCloudApiMetadataTest extends BaseApiMetadataTest {
 
+   public VCloudApiMetadataTest() {
+      super(new VCloudApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/apis/walrus/src/main/java/org/jclouds/walrus/WalrusApiMetadata.java b/apis/walrus/src/main/java/org/jclouds/walrus/WalrusApiMetadata.java
new file mode 100644
index 0000000..50ac5ac
--- /dev/null
+++ b/apis/walrus/src/main/java/org/jclouds/walrus/WalrusApiMetadata.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.walrus;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.s3.S3ApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the Walrus (S3 clone) api.
+ * 
+ * @author Adrian Cole
+ */
+public class WalrusApiMetadata extends S3ApiMetadata {
+
+   public WalrusApiMetadata() {
+      this(builder().fromApiMetadata(new S3ApiMetadata())
+            .id("walrus")
+            .name("Walrus (S3 clone) API"));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected WalrusApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends S3ApiMetadataBuilder<ConcreteBuilder> {
+
+      @Override
+      public WalrusApiMetadata build() {
+         return new WalrusApiMetadata(this);
+      }
+   }
+
+   private static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/apis/walrus/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/walrus/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..615ba3f
--- /dev/null
+++ b/apis/walrus/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.walrus.WalrusApiMetadata
\ No newline at end of file
diff --git a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java b/apis/walrus/src/test/java/org/jclouds/walrus/WalrusApiMetadataTest.java
similarity index 69%
copy from providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java
copy to apis/walrus/src/test/java/org/jclouds/walrus/WalrusApiMetadataTest.java
index c0d2996..ca5dbf8 100644
--- a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/ProvidersInPropertiesTest.java
+++ b/apis/walrus/src/test/java/org/jclouds/walrus/WalrusApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.cloudonestorage;
+package org.jclouds.walrus;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "cloudonestorage") : providers;
-   }
+@Test(groups = "unit", testName = "WalrusApiMetadataTest")
+public class WalrusApiMetadataTest extends BaseApiMetadataTest {
 
+   public WalrusApiMetadataTest() {
+      super(new WalrusApiMetadata(), ApiType.BLOBSTORE);
+   }
 }
diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientApiMetadata.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientApiMetadata.java
new file mode 100644
index 0000000..815114d
--- /dev/null
+++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientApiMetadata.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.blobstore;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for jclouds in-memory (Transient) API
+ * 
+ * @author Adrian Cole
+ */
+public class TransientApiMetadata extends BaseApiMetadata {
+
+   public TransientApiMetadata() {
+      this(builder()
+            .id("transient")
+            .type(ApiType.BLOBSTORE)
+            .name("in-memory (Transient) API")
+            .identityName("Unused")
+            .documentation(URI.create("http://www.jclouds.org/documentation/userguide/blobstore-guide")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected TransientApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public TransientApiMetadata build() {
+         return new TransientApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/blobstore/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/blobstore/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..a5bb992
--- /dev/null
+++ b/blobstore/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.blobstore.TransientApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/blobstore/src/test/java/org/jclouds/blobstore/TransientApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to blobstore/src/test/java/org/jclouds/blobstore/TransientApiMetadataTest.java
index ace0f456..98fa876 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/blobstore/src/test/java/org/jclouds/blobstore/TransientApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.blobstore;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "TransientApiMetadataTest")
+public class TransientApiMetadataTest extends BaseApiMetadataTest {
 
+   public TransientApiMetadataTest() {
+      super(new TransientApiMetadata(), ApiType.BLOBSTORE);
+   }
 }
diff --git a/common/aws/src/main/java/org/jclouds/aws/filters/FormSigner.java b/common/aws/src/main/java/org/jclouds/aws/filters/FormSigner.java
index beb9c0d..cb8e07f 100644
--- a/common/aws/src/main/java/org/jclouds/aws/filters/FormSigner.java
+++ b/common/aws/src/main/java/org/jclouds/aws/filters/FormSigner.java
@@ -53,10 +53,14 @@
 import org.jclouds.io.InputSuppliers;
 import org.jclouds.logging.Logger;
 import org.jclouds.rest.RequestSigner;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.jclouds.rest.annotations.Credential;
+import org.jclouds.rest.annotations.Identity;
 import org.jclouds.util.Strings2;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
 
 /**
@@ -73,6 +77,7 @@
    public static String[] mandatoryParametersForSignature = new String[] { ACTION, SIGNATURE_METHOD, SIGNATURE_VERSION,
             VERSION };
    private final SignatureWire signatureWire;
+   private final String apiVersion;
    private final String accessKey;
    private final String secretKey;
    private final Provider<String> dateService;
@@ -84,10 +89,11 @@
    private Logger signatureLog = Logger.NULL;
 
    @Inject
-   public FormSigner(SignatureWire signatureWire, @Named(Constants.PROPERTY_IDENTITY) String accessKey,
-            @Named(Constants.PROPERTY_CREDENTIAL) String secretKey, @TimeStamp Provider<String> dateService,
+   public FormSigner(SignatureWire signatureWire, @ApiVersion String apiVersion, @Identity String accessKey,
+            @Credential String secretKey, @TimeStamp Provider<String> dateService,
             Crypto crypto, HttpUtils utils) {
       this.signatureWire = signatureWire;
+      this.apiVersion = apiVersion;
       this.accessKey = accessKey;
       this.secretKey = secretKey;
       this.dateService = dateService;
@@ -99,6 +105,7 @@
       checkNotNull(request.getFirstHeaderOrNull(HttpHeaders.HOST), "request is not ready to sign; host not present");
       Multimap<String, String> decodedParams = ModifyRequest.parseQueryToMap(request.getPayload().getRawContent()
                .toString());
+      decodedParams.replaceValues(VERSION, ImmutableSet.of(apiVersion));
       addSigningParams(decodedParams);
       validateParams(decodedParams);
       String stringToSign = createStringToSign(request, decodedParams);
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 8c39c3b..7471bf4 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
@@ -102,7 +102,7 @@
                exception = new UnsupportedOperationException(message, exception);
             else if ("AddressLimitExceeded".equals(errorCode))
                exception = new InsufficientResourcesException(message, exception);
-            else if (errorCode != null && (errorCode.endsWith("NotFound") || errorCode.endsWith(".Unknown")))
+            else if (errorCode != null && (errorCode.indexOf("NotFound") != -1 || errorCode.endsWith(".Unknown")))
                exception = new ResourceNotFoundException(message, exception);
             else if ("IncorrectState".equals(errorCode)
                      || (errorCode != null && (error.getCode().endsWith(".Duplicate") | error.getCode().endsWith(
diff --git a/common/aws/src/test/java/org/jclouds/aws/filters/FormSignerTest.java b/common/aws/src/test/java/org/jclouds/aws/filters/FormSignerTest.java
index ba64c54..6cfffe4 100644
--- a/common/aws/src/test/java/org/jclouds/aws/filters/FormSignerTest.java
+++ b/common/aws/src/test/java/org/jclouds/aws/filters/FormSignerTest.java
@@ -20,16 +20,22 @@
 import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
 import static org.testng.Assert.assertEquals;
 
+import java.net.URI;
+
+import javax.ws.rs.core.HttpHeaders;
+
 import org.jclouds.PropertiesBuilder;
 import org.jclouds.date.TimeStamp;
+import org.jclouds.http.HttpRequest;
 import org.jclouds.http.IntegrationTestAsyncClient;
 import org.jclouds.http.IntegrationTestClient;
+import org.jclouds.io.Payloads;
 import org.jclouds.logging.config.NullLoggingModule;
-import org.jclouds.rest.BaseRestClientTest.MockModule;
 import org.jclouds.rest.RequestSigner;
 import org.jclouds.rest.RestContextBuilder;
 import org.jclouds.rest.RestContextFactory;
 import org.jclouds.rest.RestContextSpec;
+import org.jclouds.rest.BaseRestClientTest.MockModule;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -60,6 +66,19 @@
                         }
 
                      }));
+   @Test
+   void testBuildCanonicalizedStringSetsVersion() {
+      FormSigner filter = RestContextFactory.createContextBuilder(DUMMY_SPEC).buildInjector().getInstance(
+               FormSigner.class);
+
+      assertEquals(
+               filter.filter(
+                        HttpRequest.builder().method("GET").endpoint(URI.create("http://localhost")).headers(
+                                 ImmutableMultimap.of(HttpHeaders.HOST, "localhost")).payload(
+                                 Payloads.newStringPayload("Action=DescribeImages&ImageId.1=ami-2bb65342")).build())
+                        .getPayload().getRawContent(),
+               "Action=DescribeImages&ImageId.1=ami-2bb65342&Signature=ugnt4m2eHE7Ka%2FvXTr9EhKZq7bhxOfvW0y4pAEqF97w%3D&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2009-11-08T15%3A54%3A08.897Z&Version=apiVersion&AWSAccessKeyId=identity");
+   }
 
    @Test
    void testBuildCanonicalizedString() {
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 e65a674..6df2308 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
@@ -68,6 +68,12 @@
       assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "",
                "<Error><Code>LoadBalancerNotFound</Code></Error>", ResourceNotFoundException.class);
    }
+   
+   @Test
+   public void test400WithSecurityGroupNotFoundForProjectSetsResourceNotFoundException() {
+      assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "",
+               "<Error><Code>SecurityGroupNotFoundForProject</Code></Error>", ResourceNotFoundException.class);
+   }
 
    @Test
    public void test400WithUnsupportedCodeMakesUnsupportedOperationException() {
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 4930f58..272d4a8 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
@@ -617,7 +617,7 @@
       Iterable<? extends Image> supportedImages = filter(images, imagePredicate);
       if (size(supportedImages) == 0) {
          if (imagePredicate == idPredicate) {
-            throw new NoSuchElementException(format("%s not found", idPredicate));
+            throwNoSuchElementExceptionAfterLoggingImageIds(format("%s not found", idPredicate), images);
          } else {
             throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched predicate: %s", imagePredicate),
                      images);
diff --git a/compute/src/main/java/org/jclouds/compute/stub/StubApiMetadata.java b/compute/src/main/java/org/jclouds/compute/stub/StubApiMetadata.java
new file mode 100644
index 0000000..30b6ffc
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/stub/StubApiMetadata.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.compute.stub;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for jclouds in-memory (Stub) API
+ * 
+ * @author Adrian Cole
+ */
+public class StubApiMetadata extends BaseApiMetadata {
+
+   public StubApiMetadata() {
+      this(builder()
+            .id("stub")
+            .type(ApiType.COMPUTE)
+            .name("in-memory (Stub) API")
+            .identityName("Unused")
+            .documentation(URI.create("http://www.jclouds.org/documentation/userguide/compute")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected StubApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public StubApiMetadata build() {
+         return new StubApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/compute/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/compute/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..542beaa
--- /dev/null
+++ b/compute/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.compute.stub.StubApiMetadata
\ No newline at end of file
diff --git a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java
index 7be16f5..f65cbed 100644
--- a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java
@@ -61,6 +61,7 @@
 import java.util.SortedSet;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CancellationException;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -83,12 +84,14 @@
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LocationScope;
 import org.jclouds.domain.LoginCredentials;
+import org.jclouds.io.CopyInputStreamInputSupplierMap;
 import org.jclouds.logging.LoggingModules;
 import org.jclouds.logging.config.LoggingModule;
 import org.jclouds.net.IPSocket;
 import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.predicates.SocketOpen;
 import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.config.CredentialStoreModule;
 import org.jclouds.scriptbuilder.domain.SaveHttpResponseTo;
 import org.jclouds.scriptbuilder.domain.Statements;
 import org.jclouds.scriptbuilder.statements.java.InstallJDK;
@@ -107,6 +110,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.io.InputSupplier;
 import com.google.common.net.InetAddresses;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.gson.annotations.SerializedName;
@@ -130,7 +134,10 @@
 
    protected Template template;
    protected Map<String, String> keyPair;
-
+   // isolate tests from eachother, as default credentialStore is static
+   protected Module credentialStoreModule = new CredentialStoreModule(
+            new CopyInputStreamInputSupplierMap(new ConcurrentHashMap<String, InputSupplier<InputStream>>()));
+            
    @BeforeGroups(groups = { "integration", "live" })
    public void setupClient() throws InterruptedException, ExecutionException, TimeoutException, IOException {
       setServiceDefaults();
@@ -189,7 +196,7 @@
          overrides.setProperty(provider + ".identity", "MOMMA");
          overrides.setProperty(provider + ".credential", "MIA");
          context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider,
-               ImmutableSet.<Module> of(getLoggingModule()), overrides);
+               ImmutableSet.<Module> of(getLoggingModule(), credentialStoreModule), overrides);
          context.getComputeService().listNodes();
       } catch (AuthorizationException e) {
          throw e;
diff --git a/compute/src/test/java/org/jclouds/compute/BaseTemplateBuilderLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseTemplateBuilderLiveTest.java
index 6b79b46..8577e98 100644
--- a/compute/src/test/java/org/jclouds/compute/BaseTemplateBuilderLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/BaseTemplateBuilderLiveTest.java
@@ -22,11 +22,13 @@
 import static org.testng.Assert.assertEquals;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Properties;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -40,10 +42,12 @@
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LocationScope;
 import org.jclouds.domain.LoginCredentials;
+import org.jclouds.io.CopyInputStreamInputSupplierMap;
 import org.jclouds.json.Json;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.logging.LoggingModules;
 import org.jclouds.logging.config.LoggingModule;
+import org.jclouds.rest.config.CredentialStoreModule;
 import org.testng.annotations.AfterTest;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -55,6 +59,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
+import com.google.common.io.InputSupplier;
 import com.google.inject.Guice;
 import com.google.inject.Module;
 
@@ -269,6 +274,10 @@
    }
 
    protected void tryOverrideUsingPropertyKey(String propertyKey) {
+      // isolate tests from eachother, as default credentialStore is static
+      Module credentialStoreModule = new CredentialStoreModule(
+               new CopyInputStreamInputSupplierMap(new ConcurrentHashMap<String, InputSupplier<InputStream>>()));
+      
       ComputeServiceContext context = null;
       try {
          Properties overrides = setupProperties();
@@ -278,7 +287,7 @@
          overrides.setProperty(propertyKey + ".image.authenticate-sudo", auth + "");
 
          context = new ComputeServiceContextFactory().createContext(provider,
-               ImmutableSet.<Module> of(getLoggingModule()), overrides);
+               ImmutableSet.<Module> of(getLoggingModule(), credentialStoreModule), overrides);
 
          Iterable<String> userPass = Splitter.on(':').split(login);
          String user = Iterables.get(userPass, 0);
@@ -287,9 +296,6 @@
                LoginCredentials.builder().user(user).password(pass).authenticateSudo(auth).build());
       } finally {
          if (context != null){
-            // Need to clear persisted credentials; otherwise next time a ComputeServiceContext is created  
-            // then it will have these "foo" credentials!
-            context.credentialStore().clear();
             context.close();
          }
       }
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/compute/src/test/java/org/jclouds/compute/stub/StubApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to compute/src/test/java/org/jclouds/compute/stub/StubApiMetadataTest.java
index ace0f456..6469055 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/compute/src/test/java/org/jclouds/compute/stub/StubApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.compute.stub;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "StubApiMetadataTest")
+public class StubApiMetadataTest extends BaseApiMetadataTest {
 
+   public StubApiMetadataTest() {
+      super(new StubApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/compute/src/test/resources/initscript_with_java.sh b/compute/src/test/resources/initscript_with_java.sh
index 9ad7659..e7fc3c0 100644
--- a/compute/src/test/resources/initscript_with_java.sh
+++ b/compute/src/test/resources/initscript_with_java.sh
@@ -154,18 +154,22 @@
 
 function installOpenJDK() {
   if hash apt-get 2>/dev/null; then
-    export pkg=openjdk-7-jdk
-    apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk-*|grep -v common`
+    pkg=openjdk-7-jdk
+    (apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )) &&
+      export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk*|grep -v common`
+    # ex. lucid where jdk 7 is not present
+    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
+    test -d $JAVA_HOME || apt-get-install openjdk-6-jdk
   elif hash yum 2>/dev/null; then
     #TODO: find a jdk7 yum repo
     export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg
+    yum --nogpgcheck -y install $pkg &&
     export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
   else
     abort "we only support apt-get and yum right now... please contribute!"
     return 1
   fi
+  test -n "$JAVA_HOME" || abort "JDK installation failed!"
   ln -Fs $JAVA_HOME /usr/local/jdk 
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
diff --git a/compute/src/test/resources/initscript_with_jboss.sh b/compute/src/test/resources/initscript_with_jboss.sh
index b745f96..4186f24 100644
--- a/compute/src/test/resources/initscript_with_jboss.sh
+++ b/compute/src/test/resources/initscript_with_jboss.sh
@@ -154,18 +154,22 @@
 
 function installOpenJDK() {
   if hash apt-get 2>/dev/null; then
-    export pkg=openjdk-7-jdk
-    apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk-*|grep -v common`
+    pkg=openjdk-7-jdk
+    (apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )) &&
+      export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk*|grep -v common`
+    # ex. lucid where jdk 7 is not present
+    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
+    test -d $JAVA_HOME || apt-get-install openjdk-6-jdk
   elif hash yum 2>/dev/null; then
     #TODO: find a jdk7 yum repo
     export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg
+    yum --nogpgcheck -y install $pkg &&
     export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
   else
     abort "we only support apt-get and yum right now... please contribute!"
     return 1
   fi
+  test -n "$JAVA_HOME" || abort "JDK installation failed!"
   ln -Fs $JAVA_HOME /usr/local/jdk 
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
diff --git a/compute/src/test/resources/runscript.sh b/compute/src/test/resources/runscript.sh
index ea2874c..6d10394 100644
--- a/compute/src/test/resources/runscript.sh
+++ b/compute/src/test/resources/runscript.sh
@@ -154,18 +154,22 @@
 
 function installOpenJDK() {
   if hash apt-get 2>/dev/null; then
-    export pkg=openjdk-7-jdk
-    apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk-*|grep -v common`
+    pkg=openjdk-7-jdk
+    (apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )) &&
+      export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk*|grep -v common`
+    # ex. lucid where jdk 7 is not present
+    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
+    test -d $JAVA_HOME || apt-get-install openjdk-6-jdk
   elif hash yum 2>/dev/null; then
     #TODO: find a jdk7 yum repo
     export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg
+    yum --nogpgcheck -y install $pkg &&
     export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
   else
     abort "we only support apt-get and yum right now... please contribute!"
     return 1
   fi
+  test -n "$JAVA_HOME" || abort "JDK installation failed!"
   ln -Fs $JAVA_HOME /usr/local/jdk 
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
diff --git a/core/pom.xml b/core/pom.xml
index a6abf30..9f1b049 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -99,7 +99,7 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>11.0.2</version>
+            <version>12.0-rc1</version>
         </dependency>
     </dependencies>
 
diff --git a/core/src/main/java/org/jclouds/apis/ApiMetadata.java b/core/src/main/java/org/jclouds/apis/ApiMetadata.java
new file mode 100644
index 0000000..2f00ada
--- /dev/null
+++ b/core/src/main/java/org/jclouds/apis/ApiMetadata.java
@@ -0,0 +1,119 @@
+/**
+ * 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.net.URI;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.annotations.Beta;
+
+
+/**
+ * The ApiMetadata interface allows jclouds to provide a plugin framework for
+ * gathering cloud api metadata.
+ * 
+ * @author Jeremy Whitlock <jwhitlock@apache.org>, Adrian Cole
+ * @since 1.5
+ */
+@Beta
+public interface ApiMetadata {
+
+   public static interface Builder<B extends Builder<B>> {
+      /**
+       * @see ApiMetadata#getId()
+       */
+      B id(String id);
+
+      /**
+       * @see ApiMetadata#getName()
+       */
+      B name(String name);
+
+      /**
+       * @see ApiMetadata#getType()
+       */
+      B type(ApiType type);
+
+      /**
+       * @see ApiMetadata#getIdentityName()
+       */
+      B identityName(String identityName);
+
+      /**
+       * @see ApiMetadata#getCredentialName()
+       */
+      B credentialName(@Nullable String credentialName);
+
+      /**
+       * @see ApiMetadata#getDocumentation()
+       */
+      B documentation(URI documentation);
+
+      ApiMetadata build();
+
+      B fromApiMetadata(ApiMetadata in);
+   }
+
+   /**
+    * @see Builder
+    */
+   Builder<?> toBuilder();
+
+   /**
+    * 
+    * @return the api's unique identifier
+    */
+   public String getId();
+
+   /**
+    * 
+    * @return the name (display name) of the api
+    */
+   public String getName();
+
+   /**
+    * 
+    * @return the api's type
+    */
+   public ApiType getType();
+
+   /**
+    * 
+    * @return the name (display name) of an identity on this api (ex. user,
+    *         email, account, apikey)
+    */
+   public String getIdentityName();
+
+   /**
+    * 
+    * @return the name (display name) of a credential on this api, or null if
+    *         there is none (ex. password, secret, rsaKey)
+    */
+   @Nullable
+   public String getCredentialName();
+
+   /**
+    * 
+    * @return the url for the API documentation related to this service
+    */
+   public URI getDocumentation();
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/apis/ApiPredicates.java b/core/src/main/java/org/jclouds/apis/ApiPredicates.java
new file mode 100644
index 0000000..466de17
--- /dev/null
+++ b/core/src/main/java/org/jclouds/apis/ApiPredicates.java
@@ -0,0 +1,102 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.apis;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.util.Preconditions2;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+/**
+ * Container for api filters (predicates).
+ *
+ * @author Jeremy Whitlock <jwhitlock@apache.org>
+ */
+public class ApiPredicates {
+
+   /**
+    * Returns all apis available to jclouds regardless of type.
+    *
+    * @return all available apis
+    */
+   public static Predicate<ApiMetadata> all() {
+      return Predicates.<ApiMetadata> alwaysTrue();
+   }
+
+   /**
+    * Returns all apis with the given id.
+    *
+    * @param id
+    *           the id of the api to return
+    *
+    * @return the apis with the given id
+    */
+   public static Predicate<ApiMetadata> id(final String id) {
+      Preconditions2.checkNotEmpty(id, "id must be defined");
+      return new Predicate<ApiMetadata>() {
+         /**
+          * {@inheritDoc}
+          */
+         @Override
+         public boolean apply(ApiMetadata apiMetadata) {
+            return apiMetadata.getId().equals(id);
+         }
+
+         /**
+          * {@inheritDoc}
+          */
+         @Override
+         public String toString() {
+            return "id(" + id + ")";
+         }
+      };
+   }
+
+   /**
+    * Returns all apis with the given type.
+    *
+    * @param type
+    *           the type of the api to return
+    *
+    * @return the apis with the given type
+    */
+   public static Predicate<ApiMetadata> type(final ApiType type) {
+      checkNotNull(type, "type must be defined");
+      return new Predicate<ApiMetadata>() {
+         /**
+          * {@inheritDoc}
+          */
+         @Override
+         public boolean apply(ApiMetadata apiMetadata) {
+            return apiMetadata.getType().equals(type);
+         }
+
+         /**
+          * {@inheritDoc}
+          */
+         @Override
+         public String toString() {
+            return "type(" + type + ")";
+         }
+      };
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/apis/ApiType.java b/core/src/main/java/org/jclouds/apis/ApiType.java
new file mode 100644
index 0000000..f923d4b
--- /dev/null
+++ b/core/src/main/java/org/jclouds/apis/ApiType.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 static org.jclouds.util.Preconditions2.checkNotEmpty;
+
+import com.google.common.base.CaseFormat;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public enum ApiType {
+
+   BLOBSTORE, COMPUTE, LOADBALANCER, TABLE, QUEUE, MONITOR, UNRECOGNIZED;
+
+   @Override
+   public String toString() {
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name());
+   }
+
+   public static ApiType fromValue(String type) {
+      checkNotEmpty(type, "type must be defined");
+      try {
+         return valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, type));
+      } catch (IllegalArgumentException e) {
+         return UNRECOGNIZED;
+      }
+   }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/apis/Apis.java b/core/src/main/java/org/jclouds/apis/Apis.java
new file mode 100644
index 0000000..f2d6df4
--- /dev/null
+++ b/core/src/main/java/org/jclouds/apis/Apis.java
@@ -0,0 +1,139 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.apis;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.find;
+
+import java.util.NoSuchElementException;
+import java.util.ServiceLoader;
+
+/**
+ * The Apis class provides static methods for accessing apis.
+ * 
+ * @author Jeremy Whitlock <jwhitlock@apache.org>
+ */
+public class Apis {
+
+   /**
+    * Returns the apis located on the classpath via
+    * {@link java.util.ServiceLoader}.
+    * 
+    * @return all available apis loaded from classpath via ServiceLoader
+    */
+   private static Iterable<ApiMetadata> fromServiceLoader() {
+      return ServiceLoader.load(ApiMetadata.class);
+   }
+
+   /**
+    * Returns all available apis.
+    * 
+    * @return all available apis
+    */
+   public static Iterable<ApiMetadata> all() {
+      return fromServiceLoader();
+   }
+
+   /**
+    * Returns the first api with the provided id
+    * 
+    * @param id
+    *           the id of the api to return
+    * 
+    * @return the api with the given id
+    * 
+    * @throws NoSuchElementException
+    *            whenever there are no apis with the provided id
+    */
+   public static ApiMetadata withId(String id) throws NoSuchElementException {
+      return find(all(), ApiPredicates.id(id));
+   }
+
+   /**
+    * Returns the apis that are of type
+    * {@link org.jclouds.apis.ApiMetadata#BLOBSTORE}.
+    * 
+    * @return the blobstore apis
+    */
+   public static Iterable<ApiMetadata> allBlobStore() {
+      return filter(all(), ApiPredicates.type(ApiType.BLOBSTORE));
+   }
+
+   /**
+    * Returns the apis that are of type
+    * {@link org.jclouds.apis.ApiMetadata#COMPUTE}.
+    * 
+    * @return the compute service apis
+    */
+   public static Iterable<ApiMetadata> allCompute() {
+      return filter(all(), ApiPredicates.type(ApiType.COMPUTE));
+   }
+
+   /**
+    * Returns the apis that are of type
+    * {@link org.jclouds.apis.ApiMetadata#QUEUE}.
+    * 
+    * @return the queue service apis
+    */
+   public static Iterable<ApiMetadata> allQueue() {
+      return filter(all(), ApiPredicates.type(ApiType.QUEUE));
+   }
+
+   /**
+    * Returns the apis that are of type
+    * {@link org.jclouds.apis.ApiMetadata#TABLE}.
+    * 
+    * @return the table service apis
+    */
+   public static Iterable<ApiMetadata> allTable() {
+      return filter(all(), ApiPredicates.type(ApiType.TABLE));
+   }
+
+   /**
+    * Returns the apis that are of type
+    * {@link org.jclouds.apis.ApiMetadata#LOADBALANCER}.
+    * 
+    * @return the load balancer service apis
+    */
+   public static Iterable<ApiMetadata> allLoadBalancer() {
+      return filter(all(), ApiPredicates.type(ApiType.LOADBALANCER));
+   }
+
+   /**
+    * Returns the apis that are of type
+    * {@link org.jclouds.apis.ApiMetadata#MONITOR}.
+    * 
+    * @return the load balancer service apis
+    */
+   public static Iterable<ApiMetadata> allMonitor() {
+      return filter(all(), ApiPredicates.type(ApiType.MONITOR));
+   }
+
+   /**
+    * Returns the apis that are of the provided type.
+    * 
+    * @param type
+    *           the type to apis to return
+    * 
+    * @return the apis of the provided type
+    */
+   public static Iterable<ApiMetadata> ofType(ApiType type) {
+      return filter(all(), ApiPredicates.type(type));
+   }
+}
diff --git a/core/src/main/java/org/jclouds/apis/BaseApiMetadata.java b/core/src/main/java/org/jclouds/apis/BaseApiMetadata.java
new file mode 100644
index 0000000..c60a727
--- /dev/null
+++ b/core/src/main/java/org/jclouds/apis/BaseApiMetadata.java
@@ -0,0 +1,211 @@
+/**
+ * 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 static com.google.common.base.Objects.equal;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * The BaseApiMetadata class is an abstraction of {@link ApiMetadata} to be
+ * extended by those implementing ApiMetadata.
+ * 
+ * (Note: This class must be abstract to allow {@link java.util.ServiceLoader}
+ * to work properly.
+ * 
+ * @author Jeremy Whitlock <jwhitlock@apache.org>, Adrian Cole
+ */
+public abstract class BaseApiMetadata implements ApiMetadata {
+
+   public static abstract class Builder<B extends Builder<B>> implements ApiMetadata.Builder<B> {
+      protected String id;
+      protected String name;
+      protected ApiType type;
+      protected String identityName;
+      protected String credentialName;
+      protected URI documentation;
+
+      @SuppressWarnings("unchecked")
+      protected B self() {
+         return (B) this;
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B id(String id) {
+         this.id = checkNotNull(id, "id");
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B name(String name) {
+         this.name = checkNotNull(name, "name");
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B type(ApiType type) {
+         this.type = checkNotNull(type, "type");
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B identityName(String identityName) {
+         this.identityName = checkNotNull(identityName, "identityName");
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B credentialName(@Nullable String credentialName) {
+         this.credentialName = credentialName;
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B documentation(URI documentation) {
+         this.documentation = checkNotNull(documentation, "documentation");
+         return self();
+      }
+
+      public B fromApiMetadata(ApiMetadata in) {
+         return id(in.getId()).type(in.getType()).name(in.getName()).identityName(in.getIdentityName())
+               .credentialName(in.getCredentialName());
+      }
+   }
+
+   protected final String id;
+   protected final String name;
+   protected final ApiType type;
+   protected final String identityName;
+   protected final String credentialName;
+   protected final URI documentation;
+
+   protected BaseApiMetadata(Builder<?> builder) {
+      this.id = builder.id;
+      this.name = builder.name;
+      this.type = builder.type;
+      this.identityName = builder.identityName;
+      this.credentialName = builder.credentialName;
+      this.documentation = builder.documentation;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o)
+         return true;
+      // subclass equivalence is ok, since we don't know the classloader
+      // we'll get things from
+      if (o == null || !(o instanceof ApiMetadata))
+         return false;
+      ApiMetadata that = ApiMetadata.class.cast(o);
+      return equal(this.getId(), that.getId()) && equal(this.getName(), that.getName())
+            && equal(this.getType(), that.getType()) && equal(this.getIdentityName(), that.getIdentityName())
+            && equal(this.getCredentialName(), that.getCredentialName())
+            && equal(this.getDocumentation(), that.getDocumentation());
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects
+            .hashCode(getId(), getName(), getType(), getIdentityName(), getCredentialName(), getDocumentation());
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("id", getId()).add("name", getName()).add("type", getType())
+            .add("identityName", getIdentityName()).add("credentialName", getCredentialName())
+            .add("documentation", getDocumentation());
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getId() {
+      return id;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public ApiType getType() {
+      return type;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getIdentityName() {
+      return identityName;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getCredentialName() {
+      return credentialName;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public URI getDocumentation() {
+      return documentation;
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/providers/BaseProviderMetadata.java b/core/src/main/java/org/jclouds/providers/BaseProviderMetadata.java
index ad6b774..f9c5765 100644
--- a/core/src/main/java/org/jclouds/providers/BaseProviderMetadata.java
+++ b/core/src/main/java/org/jclouds/providers/BaseProviderMetadata.java
@@ -18,151 +18,289 @@
  */
 package org.jclouds.providers;
 
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.addAll;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+
 import java.net.URI;
 import java.util.Set;
 
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 
 /**
- * The BaseProviderMetadata class is an abstraction of {@link ProviderMetadata} to be extended by
- * those implementing ProviderMetadata.
+ * The BaseProviderMetadata class is an abstraction of {@link ProviderMetadata}
+ * to be extended by those implementing ProviderMetadata.
  * 
- * (Note: This class must be abstract to allow {@link java.util.ServiceLoader} to work properly.
+ * (Note: This class must be abstract to allow {@link java.util.ServiceLoader}
+ * to work properly.
  * 
- * @author Jeremy Whitlock <jwhitlock@apache.org>
+ * @author Adrian Cole
  */
 public abstract class BaseProviderMetadata implements ProviderMetadata {
 
-   /**
-    * @see java.lang.Object#hashCode()
-    */
-   @Override
-   public int hashCode() {
-      final int prime = 31;
-      int result = 1;
-      URI console = getConsole();
-      URI homepage = getHomepage();
-      URI docs = getApiDocumentation();
-      String id = getId();
-      String name = getName();
-      String identityName = getIdentityName();
-      String credentialName = getCredentialName();
-      String type = getType();
-      Set<String> linkedServices = getLinkedServices();
-      Set<String> iso3166Codes = getIso3166Codes();
+   public static abstract class Builder<B extends Builder<B>> implements ProviderMetadata.Builder<B> {
+      protected String id;
+      protected String name;
+      protected ApiMetadata api;
+      protected URI console;
+      protected URI homepage;
+      protected Set<String> linkedServices = newLinkedHashSet();
+      protected Set<String> iso3166Codes = newLinkedHashSet();
 
-      result = prime * result + ((console == null) ? 0 : console.hashCode());
-      result = prime * result + ((homepage == null) ? 0 : homepage.hashCode());
-      result = prime * result + ((docs == null) ? 0 : docs.hashCode());
-      result = prime * result + ((id == null) ? 0 : id.hashCode());
-      result = prime * result + ((name == null) ? 0 : name.hashCode());
-      result = prime * result + ((identityName == null) ? 0 : identityName.hashCode());
-      result = prime * result + ((credentialName == null) ? 0 : credentialName.hashCode());
-      result = prime * result + ((type == null) ? 0 : type.hashCode());
-      result = prime * result + ((linkedServices == null) ? 0 : linkedServices.hashCode());
-      result = prime * result + ((iso3166Codes == null) ? 0 : iso3166Codes.hashCode());
+      @SuppressWarnings("unchecked")
+      protected B self() {
+         return (B) this;
+      }
 
-      return result;
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B id(String id) {
+         this.id = checkNotNull(id, "id");
+         return linkedService(id);
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B name(String name) {
+         this.name = checkNotNull(name, "name");
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B api(ApiMetadata api) {
+         this.api = checkNotNull(api, "api");
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B console(@Nullable URI console) {
+         this.console = console;
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B homepage(URI homepage) {
+         this.homepage = homepage;
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B linkedServices(Iterable<String> linkedServices) {
+         addAll(this.linkedServices, checkNotNull(linkedServices, "linkedServices"));
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B linkedServices(String... linkedServices) {
+         return linkedServices(ImmutableSet.copyOf(checkNotNull(linkedServices, "linkedServices")));
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B linkedService(String linkedService) {
+         this.linkedServices.add(checkNotNull(linkedService, "linkedService"));
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B iso3166Codes(Iterable<String> iso3166Codes) {
+         addAll(this.iso3166Codes, checkNotNull(iso3166Codes, "iso3166Codes"));
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B iso3166Codes(String... iso3166Codes) {
+         return iso3166Codes(ImmutableSet.copyOf(checkNotNull(iso3166Codes, "iso3166Codes")));
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B iso3166Code(String iso3166Code) {
+         this.iso3166Codes.add(checkNotNull(iso3166Code, "iso3166Code"));
+         return self();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public B fromProviderMetadata(ProviderMetadata in) {
+         return id(in.getId()).name(in.getName()).api(in.getApi()).console(in.getConsole()).homepage(in.getHomepage())
+               .linkedServices(in.getLinkedServices()).iso3166Codes(in.getIso3166Codes());
+      }
    }
 
-   /**
-    * @see java.lang.Object#equals(java.lang.Object)
-    */
+   protected final String id;
+   protected final String name;
+   protected final ApiMetadata api;
+   protected final URI homepage;
+   protected final URI console;
+   protected final Set<String> linkedServices;
+   protected final Set<String> iso3166Codes;
+
+   protected BaseProviderMetadata(Builder<?> builder) {
+      this.id = builder.id;
+      this.name = builder.name;
+      this.api = builder.api;
+      this.console = builder.console;
+      this.homepage = builder.homepage;
+      this.linkedServices = ImmutableSet.copyOf(builder.linkedServices);
+      this.iso3166Codes = ImmutableSet.copyOf(builder.iso3166Codes);
+   }
+
    @Override
-   public boolean equals(Object obj) {
-      URI tConsole = getConsole();
-      URI tHomepage = getHomepage();
-      URI tDocs = getApiDocumentation();
-      String tId = getId();
-      String tName = getName();
-      String tIdentityName = getIdentityName();
-      String tCredentialName = getCredentialName();
-      String tType = getType();
-      Set<String> tLinkedServices = getLinkedServices();
-      Set<String> tIso3166Codes = getIso3166Codes();
-
-      if (this == obj)
+   public boolean equals(Object o) {
+      if (this == o)
          return true;
-      if (obj == null)
+      // subclass equivalence is ok, since we don't know the classloader
+      // we'll get things from
+      if (o == null || !(o instanceof ProviderMetadata))
          return false;
-      if (getClass() != obj.getClass())
-         return false;
+      ProviderMetadata that = ProviderMetadata.class.cast(o);
+      return equal(this.getId(), that.getId()) && equal(this.getName(), that.getName())
+            && equal(this.getApi(), that.getApi()) && equal(this.getConsole(), that.getConsole())
+            && equal(this.getHomepage(), that.getHomepage())
+            && equal(this.getLinkedServices(), that.getLinkedServices())
+            && equal(this.getIso3166Codes(), that.getIso3166Codes());
+   }
 
-      ProviderMetadata other = (ProviderMetadata) obj;
-      URI oConsole = other.getConsole();
-      URI oHomepage = other.getHomepage();
-      URI oDocs = other.getApiDocumentation();
-      String oId = other.getId();
-      String oName = other.getName();
-      String oIdentityName = other.getIdentityName();
-      String oCredentialName = other.getCredentialName();
-      String oType = other.getType();
-      Set<String> oLinkedServices = other.getLinkedServices();
-      Set<String> oIso3166Codes = other.getIso3166Codes();
-
-      if (tConsole == null) {
-         if (oConsole != null)
-            return false;
-      } else if (!tConsole.equals(oConsole))
-         return false;
-      if (tDocs == null) {
-         if (oDocs != null)
-            return false;
-      } else if (!tDocs.equals(oDocs))
-         return false;
-      if (tHomepage == null) {
-         if (oHomepage != null)
-            return false;
-      } else if (!tHomepage.equals(oHomepage))
-         return false;
-      if (tId == null) {
-         if (oId != null)
-            return false;
-      } else if (!tId.equals(oId))
-         return false;
-      if (tName == null) {
-         if (oName != null)
-            return false;
-      } else if (!tName.equals(oName))
-         return false;
-      if (tIdentityName == null) {
-         if (oIdentityName != null)
-            return false;
-      } else if (!tIdentityName.equals(oIdentityName))
-         return false;
-      if (tCredentialName == null) {
-         if (oCredentialName != null)
-            return false;
-      } else if (!tCredentialName.equals(oCredentialName))
-         return false;
-      if (tType == null) {
-         if (oType != null)
-            return false;
-      } else if (!tType.equals(oType))
-         return false;
-      if (tLinkedServices == null) {
-         if (oLinkedServices != null)
-            return false;
-      } else if (!tLinkedServices.equals(oLinkedServices))
-         return false;
-      if (tIso3166Codes == null) {
-         if (oIso3166Codes != null)
-            return false;
-      } else if (!tIso3166Codes.equals(oIso3166Codes))
-         return false;
-      return true;
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(getId(), getName(), getApi(), getConsole(), getHomepage(), getLinkedServices(),
+            getIso3166Codes());
    }
 
    @Override
    public String toString() {
-      return "[id=" + getId() + ", type=" + getType() + ", name=" + getName() + ", identityName=" + getIdentityName()
-               + ", credentialName=" + getCredentialName() + ", homePage=" + getHomepage() + ", console="
-               + getConsole() + ", apiDocs=" + getApiDocumentation() + ", linkedServices=" + getLinkedServices() +
-               ", iso3166Codes=" + getIso3166Codes() + "]";
+      return string().toString();
    }
 
+   public ToStringHelper string() {
+      return Objects.toStringHelper("").add("id", getId()).add("name", getName()).add("api", getApi())
+            .add("console", getConsole()).add("homepage", getHomepage()).add("linkedServices", getLinkedServices())
+            .add("iso3166Codes", getIso3166Codes());
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getId() {
+      return id;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public ApiMetadata getApi() {
+      return api;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public URI getConsole() {
+      return console;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public URI getHomepage() {
+      return homepage;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    @Override
    public Set<String> getLinkedServices() {
-      return ImmutableSet.of(getId());
+      return linkedServices;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<String> getIso3166Codes() {
+      return iso3166Codes;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getType() {
+      return getApi().getType().toString();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getIdentityName() {
+      return getApi().getIdentityName();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getCredentialName() {
+      return getApi().getCredentialName();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public URI getApiDocumentation() {
+      return getApi().getDocumentation();
    }
 }
diff --git a/core/src/main/java/org/jclouds/providers/ProviderMetadata.java b/core/src/main/java/org/jclouds/providers/ProviderMetadata.java
index 3fec308..8542fd0 100644
--- a/core/src/main/java/org/jclouds/providers/ProviderMetadata.java
+++ b/core/src/main/java/org/jclouds/providers/ProviderMetadata.java
@@ -21,55 +21,176 @@
 import java.net.URI;
 import java.util.Set;
 
+import org.jclouds.apis.ApiMetadata;
 import org.jclouds.javax.annotation.Nullable;
 
 /**
- * The ProviderMetadata interface allows jclouds to provide a plugin framework for gathering cloud
- * provider metadata.
+ * The ProviderMetadata interface allows jclouds to provide a plugin framework
+ * for gathering cloud provider metadata.
  * 
  * @author Jeremy Whitlock <jwhitlock@apache.org>
  */
 public interface ProviderMetadata {
-
+   /**
+    * @see ApiMetadata#BLOBSTORE_TYPE
+    */
+   @Deprecated
    public static final String BLOBSTORE_TYPE = "blobstore";
+   /**
+    * @see ApiMetadata#COMPUTE_TYPE
+    */
+   @Deprecated
    public static final String COMPUTE_TYPE = "compute";
+   /**
+    * @see ApiMetadata#LOADBALANCER_TYPE
+    */
+   @Deprecated
    public static final String LOADBALANCER_TYPE = "loadbalancer";
+   /**
+    * @see ApiMetadata#TABLE_TYPE
+    */
+   @Deprecated
    public static final String TABLE_TYPE = "table";
+   /**
+    * @see ApiMetadata#QUEUE_TYPE
+    */
+   @Deprecated
    public static final String QUEUE_TYPE = "queue";
+   /**
+    * @see ApiMetadata#MONITOR_TYPE
+    */
+   @Deprecated
    public static final String MONITOR_TYPE = "monitor";
 
    /**
     * 
+    * @author Adrian Cole
+    * @since 1.5
+    */
+   public static interface Builder<B extends Builder<B>> {
+      /**
+       * @see ProviderMetadata#getId()
+       */
+      B id(String id);
+
+      /**
+       * @see ProviderMetadata#getName()
+       */
+      B name(String name);
+
+      /**
+       * @see ProviderMetadata#getApi()
+       */
+      B api(ApiMetadata api);
+
+      /**
+       * @see ProviderMetadata#getConsole()
+       */
+      B console(@Nullable URI console);
+
+      /**
+       * @see ProviderMetadata#getHomepage()
+       */
+      B homepage(@Nullable URI homepage);
+
+      /**
+       * @see ProviderMetadata#getLinkedServices()
+       */
+      B linkedServices(Iterable<String> linkedServices);
+
+      /**
+       * @see ProviderMetadata#getLinkedServices()
+       */
+      B linkedServices(String... linkedServices);
+
+      /**
+       * @see ProviderMetadata#getLinkedServices()
+       */
+      B linkedService(String linkedService);
+
+      /**
+       * @see ProviderMetadata#getIso3166Code()
+       */
+      B iso3166Codes(Iterable<String> iso3166Codes);
+
+      /**
+       * @see ProviderMetadata#getIso3166Code()
+       */
+      B iso3166Codes(String... iso3166Codes);
+
+      /**
+       * @see ProviderMetadata#getIso3166Code()
+       */
+      B iso3166Code(String iso3166Code);
+
+      ProviderMetadata build();
+
+      B fromProviderMetadata(ProviderMetadata in);
+   }
+
+   /**
+    * @see Builder
+    * @since 1.5
+    */
+   Builder<?> toBuilder();
+
+   /**
+    * 
     * @return the provider's unique identifier
     */
    public String getId();
 
    /**
     * 
-    * @return the provider's type
-    */
-   public String getType();
-
-   /**
-    * 
     * @return the name (display name) of the provider
     */
    public String getName();
 
    /**
     * 
-    * @return the name (display name) of an identity on this provider (ex. user, email, account,
-    *         apikey)
+    * @see #getApi()
+    * @see ApiMetadata#getType
     */
+   @Deprecated
+   public String getType();
+
+   /**
+    * 
+    * @return the provider's api
+    * @since 1.5
+    */
+   public ApiMetadata getApi();
+
+   /**
+    * 
+    * @see #getApi()
+    * @see ApiMetadata#getIdentityName
+    */
+   @Deprecated
    public String getIdentityName();
 
    /**
     * 
-    * @return the name (display name) of a credential on this provider, or null if there is none
-    *         (ex. password, secret, rsaKey)
+    * @see #getApi()
+    * @see ApiMetadata#getCredentialName
+    */
+   @Deprecated
+   public String getCredentialName();
+
+   /**
+    * 
+    * @see #getApi()
+    * @see ApiMetadata#getDocumentation
+    */
+   @Deprecated
+   public URI getApiDocumentation();
+
+   /**
+    * 
+    * @return the url for the provider's console, or null if one doesn't exist
     */
    @Nullable
-   public String getCredentialName();
+   public URI getConsole();
 
    /**
     * 
@@ -79,18 +200,6 @@
 
    /**
     * 
-    * @return the url for the provider's console
-    */
-   public URI getConsole();
-
-   /**
-    * 
-    * @return the url for the API documentation related to this service
-    */
-   public URI getApiDocumentation();
-
-   /**
-    * 
     * @return all known services linked to the same account on this provider
     */
    public Set<String> getLinkedServices();
diff --git a/core/src/main/java/org/jclouds/providers/ProviderPredicates.java b/core/src/main/java/org/jclouds/providers/ProviderPredicates.java
index 8df155c..f3b8195 100644
--- a/core/src/main/java/org/jclouds/providers/ProviderPredicates.java
+++ b/core/src/main/java/org/jclouds/providers/ProviderPredicates.java
@@ -18,6 +18,8 @@
  */
 package org.jclouds.providers;
 
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
 import org.jclouds.util.Preconditions2;
 
 import com.google.common.base.Preconditions;
@@ -26,14 +28,14 @@
 
 /**
  * Container for provider filters (predicates).
- *
+ * 
  * @author Jeremy Whitlock <jwhitlock@apache.org>
  */
 public class ProviderPredicates {
 
    /**
     * Returns all providers available to jclouds regardless of type.
-    *
+    * 
     * @return all available providers
     */
    public static Predicate<ProviderMetadata> all() {
@@ -42,10 +44,10 @@
 
    /**
     * Returns all providers with the given id.
-    *
+    * 
     * @param id
     *           the id of the provider to return
-    *
+    * 
     * @return the providers with the given id
     */
    public static Predicate<ProviderMetadata> id(final String id) {
@@ -71,21 +73,21 @@
 
    /**
     * Returns all providers with the given type.
-    *
+    * 
     * @param type
     *           the type of the provider to return
-    *
+    * 
     * @return the providers with the given type
     */
-   public static Predicate<ProviderMetadata> type(final String type) {
-      Preconditions2.checkNotEmpty(type, "type must be defined");
+   public static Predicate<ProviderMetadata> type(final ApiType type) {
+      Preconditions.checkNotNull(type, "type must be defined");
       return new Predicate<ProviderMetadata>() {
          /**
           * {@inheritDoc}
           */
          @Override
          public boolean apply(ProviderMetadata providerMetadata) {
-            return providerMetadata.getType().equals(type);
+            return providerMetadata.getApi().getType().equals(type);
          }
 
          /**
@@ -99,10 +101,19 @@
    }
 
    /**
-    * Returns the providers that are bound to the same location as the given ISO 3166 code.
+    * @see #type(ApiMetadata)
+    */
+   @Deprecated
+   public static Predicate<ProviderMetadata> type(final String type) {
+      return type(ApiType.fromValue(type));
+   }
+
+   /**
+    * Returns the providers that are bound to the same location as the given ISO
+    * 3166 code.
     * 
     * @param isoCode
-    *                the ISO 3166 code to filter providers by
+    *           the ISO 3166 code to filter providers by
     * 
     * @return the providers with the given ISO 3166 code
     */
@@ -129,10 +140,11 @@
    }
 
    /**
-    * Return all providers that have at least one ISO 3166 code in common with the given provider metadata.
+    * Return all providers that have at least one ISO 3166 code in common with
+    * the given provider metadata.
     * 
     * @param refProviderMetadata
-    *                            the provider metadata to use to filter providers by
+    *           the provider metadata to use to filter providers by
     * 
     * @return the providers that have at least one ISO 3166 code in common
     */
@@ -146,10 +158,11 @@
          @Override
          public boolean apply(ProviderMetadata providerMetadata) {
             for (String refIso3166Code : refProviderMetadata.getIso3166Codes()) {
-               // Return only if the potential provider contains the same ISO 3166 code and the provider and
+               // Return only if the potential provider contains the same ISO
+               // 3166 code and the provider and
                // reference provider are not the same.
-               if (providerContainsIso3166Code(providerMetadata, refIso3166Code) &&
-                     !refProviderMetadata.equals(providerMetadata)) {
+               if (providerContainsIso3166Code(providerMetadata, refIso3166Code)
+                     && !refProviderMetadata.equals(providerMetadata)) {
                   return true;
                }
             }
@@ -167,19 +180,20 @@
    }
 
    /**
-    * Returns whether or not the provided provider contains the ISO 3166 code provider or is within the same
-    * "global" region, like "US" would contain "US-*".
+    * Returns whether or not the provided provider contains the ISO 3166 code
+    * provider or is within the same "global" region, like "US" would contain
+    * "US-*".
     * 
     * @param providerMetadata
-    *                         the provider metadata to search
+    *           the provider metadata to search
     * @param iso3166Code
-    *                    the ISO 3166 code to search the provider metadata for
+    *           the ISO 3166 code to search the provider metadata for
     * 
     * @return the result
     */
    private static boolean providerContainsIso3166Code(ProviderMetadata providerMetadata, String iso3166Code) {
       for (String availCode : providerMetadata.getIso3166Codes()) {
-         if(iso3166Code.indexOf('-') == -1) {
+         if (iso3166Code.indexOf('-') == -1) {
             if (availCode.startsWith(iso3166Code + "-")) {
                return true;
             }
@@ -190,4 +204,33 @@
 
       return false;
    }
+
+   /**
+    * Returns all providers with an instance of the given api.
+    * 
+    * @param apiClass
+    *           the api of the provider to return
+    * 
+    * @return the providers with the given api
+    */
+   public static Predicate<ProviderMetadata> apiInstanceOf(final Class<? extends ApiMetadata> apiClass) {
+      Preconditions.checkNotNull(apiClass, "api must be defined");
+      return new Predicate<ProviderMetadata>() {
+         /**
+          * {@inheritDoc}
+          */
+         @Override
+         public boolean apply(ProviderMetadata providerMetadata) {
+            return Predicates.instanceOf(apiClass).apply(providerMetadata.getApi());
+         }
+
+         /**
+          * {@inheritDoc}
+          */
+         @Override
+         public String toString() {
+            return "apiInstanceOf(" + apiClass + ")";
+         }
+      };
+   }
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/providers/Providers.java b/core/src/main/java/org/jclouds/providers/Providers.java
index 8716fed..5b883ad 100644
--- a/core/src/main/java/org/jclouds/providers/Providers.java
+++ b/core/src/main/java/org/jclouds/providers/Providers.java
@@ -24,6 +24,10 @@
 import java.util.NoSuchElementException;
 import java.util.ServiceLoader;
 
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+
+import com.google.common.base.Preconditions;
 import com.google.common.base.Predicates;
 
 /**
@@ -34,7 +38,8 @@
 public class Providers {
 
    /**
-    * Returns the providers located on the classpath via {@link java.util.ServiceLoader}.
+    * Returns the providers located on the classpath via
+    * {@link java.util.ServiceLoader}.
     * 
     * @return all available providers loaded from classpath via ServiceLoader
     */
@@ -68,52 +73,52 @@
 
    /**
     * Returns the providers that are of type
-    * {@link org.jclouds.providers.ProviderMetadata#BLOBSTORE_TYPE}.
+    * {@link org.jclouds.providers.ProviderMetadata#BLOBSTORE}.
     * 
     * @return the blobstore providers
     */
    public static Iterable<ProviderMetadata> allBlobStore() {
-      return filter(all(), ProviderPredicates.type(ProviderMetadata.BLOBSTORE_TYPE));
+      return filter(all(), ProviderPredicates.type(ApiType.BLOBSTORE));
    }
 
    /**
     * Returns the providers that are of type
-    * {@link org.jclouds.providers.ProviderMetadata#COMPUTE_TYPE}.
+    * {@link org.jclouds.providers.ProviderMetadata#COMPUTE}.
     * 
     * @return the compute service providers
     */
    public static Iterable<ProviderMetadata> allCompute() {
-      return filter(all(), ProviderPredicates.type(ProviderMetadata.COMPUTE_TYPE));
+      return filter(all(), ProviderPredicates.type(ApiType.COMPUTE));
    }
 
    /**
     * Returns the providers that are of type
-    * {@link org.jclouds.providers.ProviderMetadata#QUEUE_TYPE}.
+    * {@link org.jclouds.providers.ProviderMetadata#QUEUE}.
     * 
     * @return the queue service providers
     */
    public static Iterable<ProviderMetadata> allQueue() {
-      return filter(all(), ProviderPredicates.type(ProviderMetadata.QUEUE_TYPE));
+      return filter(all(), ProviderPredicates.type(ApiType.QUEUE));
    }
 
    /**
     * Returns the providers that are of type
-    * {@link org.jclouds.providers.ProviderMetadata#TABLE_TYPE}.
+    * {@link org.jclouds.providers.ProviderMetadata#TABLE}.
     * 
     * @return the table service providers
     */
    public static Iterable<ProviderMetadata> allTable() {
-      return filter(all(), ProviderPredicates.type(ProviderMetadata.TABLE_TYPE));
+      return filter(all(), ProviderPredicates.type(ApiType.TABLE));
    }
 
    /**
     * Returns the providers that are of type
-    * {@link org.jclouds.providers.ProviderMetadata#LOADBALANCER_TYPE}.
+    * {@link org.jclouds.providers.ProviderMetadata#LOADBALANCER}.
     * 
     * @return the load balancer service providers
     */
    public static Iterable<ProviderMetadata> allLoadBalancer() {
-      return filter(all(), ProviderPredicates.type(ProviderMetadata.LOADBALANCER_TYPE));
+      return filter(all(), ProviderPredicates.type(ApiType.LOADBALANCER));
    }
 
    /**
@@ -124,15 +129,37 @@
     * 
     * @return the providers of the provided type
     */
-   public static Iterable<ProviderMetadata> ofType(String type) {
+   public static Iterable<ProviderMetadata> ofType(ApiType type) {
       return filter(all(), ProviderPredicates.type(type));
    }
 
    /**
-    * Returns the providers that are bound to the same location as the given ISO 3166 code regardless of type.
+    * Returns the providers that are of the provided api.
+    * 
+    * @param api
+    *           the api to providers to return
+    * 
+    * @return the providers of the provided api
+    */
+   public static Iterable<ProviderMetadata> ofApi(ApiMetadata api) {
+      Preconditions.checkNotNull(api, "api must be defined");
+      return filter(all(), ProviderPredicates.apiInstanceOf(api.getClass()));
+   }
+   
+   /**
+    * @see #ofType(ApiMetadata)
+    */
+   @Deprecated
+   public static Iterable<ProviderMetadata> ofType(String type) {
+      return ofType(ApiType.fromValue(type));
+   }
+
+   /**
+    * Returns the providers that are bound to the same location as the given ISO
+    * 3166 code regardless of type.
     * 
     * @param isoCode
-    *                the ISO 3166 code to filter providers by
+    *           the ISO 3166 code to filter providers by
     * 
     * @return the providers bound by the given ISO 3166 code
     */
@@ -141,25 +168,36 @@
    }
 
    /**
-    * Returns the providers that are bound to the same location as the given ISO 3166 code and of the given type.
+    * Returns the providers that are bound to the same location as the given ISO
+    * 3166 code and of the given type.
     * 
     * @param iso3166Code
-    *                    the ISO 3166 code to filter providers by
+    *           the ISO 3166 code to filter providers by
     * @param type
-    *             the type to filter providers by
+    *           the type to filter providers by
     * 
-    * @return the providers bound by the given ISO 3166 code and of the proper type
+    * @return the providers bound by the given ISO 3166 code and of the proper
+    *         type
     */
-   public static Iterable<ProviderMetadata> boundedByIso3166Code(String iso3166Code, String type) {
-      return filter(all(), Predicates.and(ProviderPredicates.boundedByIso3166Code(iso3166Code),
-            ProviderPredicates.type(type)));
+   public static Iterable<ProviderMetadata> boundedByIso3166Code(String iso3166Code, ApiType type) {
+      return filter(all(),
+            Predicates.and(ProviderPredicates.boundedByIso3166Code(iso3166Code), ProviderPredicates.type(type)));
    }
 
    /**
-    * Returns the providers that have at least one common ISO 3166 code in common regardless of type.
+    * @see #boundedByIso3166Code(String iso3166Code, ApiType)
+    */
+   @Deprecated
+   public static Iterable<ProviderMetadata> boundedByIso3166Code(String iso3166Code, String type) {
+      return boundedByIso3166Code(iso3166Code, ApiType.fromValue(type));
+   }
+
+   /**
+    * Returns the providers that have at least one common ISO 3166 code in
+    * common regardless of type.
     * 
     * @param providerMetadata
-    *                         the provider metadata to use to filter providers by
+    *           the provider metadata to use to filter providers by
     * 
     * @return the providers that share at least one common ISO 3166 code
     */
@@ -168,17 +206,28 @@
    }
 
    /**
-    * Returns the providers that have at least one common ISO 3166 code and are of the given type.
+    * Returns the providers that have at least one common ISO 3166 code and are
+    * of the given type.
     * 
     * @param providerMetadata
-    *                         the provider metadata to use to filter providers by
+    *           the provider metadata to use to filter providers by
     * @param type
-    *             the type to filter providers by
+    *           the type to filter providers by
     * 
-    * @return the providers that share at least one common ISO 3166 code and of the given type
+    * @return the providers that share at least one common ISO 3166 code and of
+    *         the given type
     */
-   public static Iterable<ProviderMetadata> collocatedWith(ProviderMetadata providerMetadata, String type) {
-      return filter(all(), Predicates.and(ProviderPredicates.intersectingIso3166Code(providerMetadata),
-            ProviderPredicates.type(type)));
+   public static Iterable<ProviderMetadata> collocatedWith(ProviderMetadata providerMetadata, ApiType type) {
+      return filter(all(),
+            Predicates.and(ProviderPredicates.intersectingIso3166Code(providerMetadata), ProviderPredicates.type(type)));
    }
+
+   /**
+    * @see #collocatedWith(ProviderMetadata iso3166Code, ApiType)
+    */
+   @Deprecated
+   public static Iterable<ProviderMetadata> collocatedWith(ProviderMetadata providerMetadata, String type) {
+      return collocatedWith(providerMetadata, ApiType.fromValue(type));
+   }
+
 }
diff --git a/core/src/main/resources/rest.properties b/core/src/main/resources/rest.properties
index 61150e2..81cf5fe 100644
--- a/core/src/main/resources/rest.properties
+++ b/core/src/main/resources/rest.properties
@@ -70,6 +70,9 @@
 eucalyptus.contextbuilder=org.jclouds.ec2.EC2ContextBuilder
 eucalyptus.propertiesbuilder=org.jclouds.eucalyptus.EucalyptusPropertiesBuilder
 
+openstack-nova-ec2.contextbuilder=org.jclouds.openstack.nova.ec2.NovaEC2ContextBuilder
+openstack-nova-ec2.propertiesbuilder=org.jclouds.openstack.nova.ec2.NovaEC2PropertiesBuilder
+
 eucalyptus-partnercloud-ec2.contextbuilder=org.jclouds.epc.EucalyptusPartnerCloudContextBuilder
 eucalyptus-partnercloud-ec2.propertiesbuilder=org.jclouds.epc.EucalyptusPartnerCloudPropertiesBuilder
 
diff --git a/core/src/test/java/org/jclouds/apis/ApisTest.java b/core/src/test/java/org/jclouds/apis/ApisTest.java
new file mode 100644
index 0000000..9f945ed
--- /dev/null
+++ b/core/src/test/java/org/jclouds/apis/ApisTest.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.apis;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.util.NoSuchElementException;
+
+import org.testng.annotations.Test;
+
+/**
+ * The ApisTest tests the org.jclouds.apis.Apis class.
+ * 
+ * @author Jeremy Whitlock <jwhitlock@apache.org>
+ */
+@Test(groups = "unit", testName = "ApisTest")
+public class ApisTest {
+
+   private final ApiMetadata testBlobstoreApi = new JcloudsTestBlobStoreApiMetadata();
+   private final ApiMetadata testComputeApi = new JcloudsTestComputeApiMetadata();
+   private final ApiMetadata testYetAnotherComputeApi = new JcloudsTestYetAnotherComputeApiMetadata();
+
+   @Test
+   public void testWithId() {
+      ApiMetadata apiMetadata;
+
+      try {
+         apiMetadata = Apis.withId("fake-id");
+         fail("Looking for a api with an id that doesn't exist should " + "throw an exceptoin.");
+      } catch (NoSuchElementException nsee) {
+         ; // Expected
+      }
+
+      apiMetadata = Apis.withId(testBlobstoreApi.getId());
+
+      assertEquals(testBlobstoreApi, apiMetadata);
+   }
+
+   @Test
+   public void testOfType() {
+      Iterable<ApiMetadata> apisMetadata = Apis.ofType(ApiType.BLOBSTORE);
+
+      for (ApiMetadata apiMetadata : apisMetadata) {
+         assertEquals(testBlobstoreApi, apiMetadata);
+      }
+
+      apisMetadata = Apis.ofType(ApiType.COMPUTE);
+
+      for (ApiMetadata apiMetadata : apisMetadata) {
+         if (apiMetadata.getName().equals(testComputeApi.getName())) {
+            assertEquals(testComputeApi, apiMetadata);
+         } else {
+            assertEquals(testYetAnotherComputeApi, apiMetadata);
+         }
+      }
+
+      apisMetadata = Apis.ofType(ApiType.UNRECOGNIZED);
+
+      assertEquals(false, apisMetadata.iterator().hasNext());
+   }
+
+   @Test
+   public void testAll() {
+      Iterable<ApiMetadata> apisMetadata = Apis.all();
+
+      for (ApiMetadata apiMetadata : apisMetadata) {
+         if (apiMetadata.getName().equals(testBlobstoreApi.getName())) {
+            assertEquals(testBlobstoreApi, apiMetadata);
+         } else if (apiMetadata.getName().equals(testComputeApi.getName())) {
+            assertEquals(testComputeApi, apiMetadata);
+         } else {
+            assertEquals(testYetAnotherComputeApi, apiMetadata);
+         }
+      }
+   }
+
+}
diff --git a/core/src/test/java/org/jclouds/apis/BaseApiMetadataTest.java b/core/src/test/java/org/jclouds/apis/BaseApiMetadataTest.java
new file mode 100644
index 0000000..dc46bf8
--- /dev/null
+++ b/core/src/test/java/org/jclouds/apis/BaseApiMetadataTest.java
@@ -0,0 +1,62 @@
+/**
+ * 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 static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @author Jeremy Whitlock <jwhitlock@apache.org>
+ */
+@Test(groups = "unit")
+public abstract class BaseApiMetadataTest {
+
+   private final ApiMetadata toTest;
+   private final ApiType expectedType;
+
+   public BaseApiMetadataTest(ApiMetadata toTest, ApiType expectedType) {
+      this.toTest = toTest;
+      this.expectedType = expectedType;
+   }
+
+   @Test
+   public void testWithId() {
+      ApiMetadata apiMetadata = Apis.withId(toTest.getId());
+
+      assertEquals(toTest, apiMetadata);
+   }
+
+   // it is ok to have multiple services in the same classpath (ex. ec2 vs elb)
+   @Test
+   public void testOfTypeContains() {
+      ImmutableSet<ApiMetadata> ofType = ImmutableSet.copyOf(Apis.ofType(expectedType));
+      assert ofType.contains(toTest) : String.format("%s not found in %s", toTest, ofType);
+   }
+
+   @Test
+   public void testAllContains() {
+      ImmutableSet<ApiMetadata> all = ImmutableSet.copyOf(Apis.all());
+      assert all.contains(toTest) : String.format("%s not found in %s", toTest, all);
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/apis/JcloudsTestBlobStoreApiMetadata.java b/core/src/test/java/org/jclouds/apis/JcloudsTestBlobStoreApiMetadata.java
new file mode 100644
index 0000000..7f1cffa
--- /dev/null
+++ b/core/src/test/java/org/jclouds/apis/JcloudsTestBlobStoreApiMetadata.java
@@ -0,0 +1,63 @@
+/**
+ * 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.net.URI;
+
+/**
+ * Implementation of @ link org.jclouds.types.ApiMetadata} for testing.
+ * 
+ * @author Jeremy Whitlock <jwhitlock@apache.org>, Adrian Cole
+ */
+public class JcloudsTestBlobStoreApiMetadata extends BaseApiMetadata {
+   
+   public JcloudsTestBlobStoreApiMetadata() {
+      this(builder()
+            .id("test-blobstore-api")
+            .type(ApiType.BLOBSTORE)
+            .name("Test Blobstore Api")
+            .identityName("user")
+            .credentialName("password")
+            .documentation(URI.create("http://jclouds.org/documentation")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected JcloudsTestBlobStoreApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public JcloudsTestBlobStoreApiMetadata build() {
+         return new JcloudsTestBlobStoreApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/apis/JcloudsTestComputeApiMetadata.java b/core/src/test/java/org/jclouds/apis/JcloudsTestComputeApiMetadata.java
new file mode 100644
index 0000000..c21f86c
--- /dev/null
+++ b/core/src/test/java/org/jclouds/apis/JcloudsTestComputeApiMetadata.java
@@ -0,0 +1,63 @@
+/**
+ * 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.net.URI;
+
+/**
+ * Implementation of @ link org.jclouds.types.ApiMetadata} for testing.
+ * 
+ * @author Jeremy Whitlock <jwhitlock@apache.org>, Adrian Cole
+ */
+public class JcloudsTestComputeApiMetadata extends BaseApiMetadata {
+   
+   public JcloudsTestComputeApiMetadata() {
+      this(builder()
+            .id("test-compute-api")
+            .type(ApiType.COMPUTE)
+            .name("Test Compute Api")
+            .identityName("user")
+            .credentialName("password")
+            .documentation(URI.create("http://jclouds.org/documentation")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected JcloudsTestComputeApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public JcloudsTestComputeApiMetadata build() {
+         return new JcloudsTestComputeApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/apis/JcloudsTestYetAnotherComputeApiMetadata.java b/core/src/test/java/org/jclouds/apis/JcloudsTestYetAnotherComputeApiMetadata.java
new file mode 100644
index 0000000..eb7c35e
--- /dev/null
+++ b/core/src/test/java/org/jclouds/apis/JcloudsTestYetAnotherComputeApiMetadata.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.apis;
+
+import java.net.URI;
+
+/**
+ * Implementation of @ link org.jclouds.types.ApiMetadata} for testing.
+ * 
+ * @author Jeremy Whitlock <jwhitlock@apache.org>
+ */
+public class JcloudsTestYetAnotherComputeApiMetadata extends BaseApiMetadata {
+   
+   public JcloudsTestYetAnotherComputeApiMetadata() {
+      this(builder()
+            .id("test-yet-another-compute-api")
+            .type(ApiType.COMPUTE)
+            .name("Test Yet Another Compute Api")
+            .identityName("user")
+            .credentialName("password")
+            .documentation(URI.create("http://jclouds.org/documentation")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected JcloudsTestYetAnotherComputeApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public JcloudsTestYetAnotherComputeApiMetadata build() {
+         return new JcloudsTestYetAnotherComputeApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/providers/BaseProviderMetadataTest.java b/core/src/test/java/org/jclouds/providers/BaseProviderMetadataTest.java
index e3e773e..9c27af3 100644
--- a/core/src/test/java/org/jclouds/providers/BaseProviderMetadataTest.java
+++ b/core/src/test/java/org/jclouds/providers/BaseProviderMetadataTest.java
@@ -18,10 +18,13 @@
  */
 package org.jclouds.providers;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static org.testng.Assert.assertEquals;
 
-import java.util.Set;
+import java.util.logging.Logger;
 
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableSet;
@@ -33,15 +36,15 @@
  */
 @Test(groups = "unit")
 public abstract class BaseProviderMetadataTest {
-   protected Set<String> allTypes = ImmutableSet.of(ProviderMetadata.BLOBSTORE_TYPE, ProviderMetadata.COMPUTE_TYPE,
-            ProviderMetadata.LOADBALANCER_TYPE, ProviderMetadata.QUEUE_TYPE, ProviderMetadata.TABLE_TYPE);
 
    private final ProviderMetadata toTest;
-   private final String expectedType;
+   private final ApiType expectedType;
+   private final ApiMetadata expectedApi;
 
-   public BaseProviderMetadataTest(ProviderMetadata toTest, String expectedType) {
-      this.toTest = toTest;
-      this.expectedType = expectedType;
+   public BaseProviderMetadataTest(ProviderMetadata toTest, ApiMetadata expectedApi) {
+      this.toTest = checkNotNull(toTest, "toTest must be defined");
+      this.expectedApi = checkNotNull(expectedApi, "expectedApi must be defined");
+      this.expectedType = expectedApi.getType();
    }
 
    @Test
@@ -51,6 +54,14 @@
       assertEquals(toTest, providerMetadata);
       assert providerMetadata.getLinkedServices().contains(toTest.getId());
    }
+   
+   @Test
+   public void testOfApiContains() {
+      if (expectedApi == null)
+         Logger.getAnonymousLogger().warning("please update your test class");
+      ImmutableSet<ProviderMetadata> ofApi = ImmutableSet.copyOf(Providers.ofApi(expectedApi));
+      assert ofApi.contains(toTest) : String.format("%s not found in %s", toTest, ofApi);
+   }
 
    // it is ok to have multiple services in the same classpath (ex. ec2 vs elb)
    @Test
diff --git a/core/src/test/java/org/jclouds/providers/JcloudsTestBlobStoreProviderMetadata.java b/core/src/test/java/org/jclouds/providers/JcloudsTestBlobStoreProviderMetadata.java
index 471951e..265079b 100644
--- a/core/src/test/java/org/jclouds/providers/JcloudsTestBlobStoreProviderMetadata.java
+++ b/core/src/test/java/org/jclouds/providers/JcloudsTestBlobStoreProviderMetadata.java
@@ -19,7 +19,8 @@
 package org.jclouds.providers;
 
 import java.net.URI;
-import java.util.Set;
+
+import org.jclouds.apis.JcloudsTestBlobStoreApiMetadata;
 
 import com.google.common.collect.ImmutableSet;
 
@@ -30,76 +31,36 @@
  */
 public class JcloudsTestBlobStoreProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "test-blobstore-provider";
+   public JcloudsTestBlobStoreProviderMetadata() {
+      this(builder()
+            .api(new JcloudsTestBlobStoreApiMetadata())
+            .id("test-blobstore-api")
+            .name("Test Blobstore Provider")
+            .homepage(URI.create("http://jclouds.org"))
+            .console(URI.create("http://jclouds.org/console"))
+            .iso3166Codes(ImmutableSet.of("US-VA", "US-CA", "US-FL")));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.BLOBSTORE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected JcloudsTestBlobStoreProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Test Blobstore Provider";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public JcloudsTestBlobStoreProviderMetadata build() {
+         return new JcloudsTestBlobStoreProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "user";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "password";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://jclouds.org");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("http://jclouds.org/console");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://jclouds.org/documentation");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-VA", "US-CA", "US-FL");
-   }
-
 }
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/providers/JcloudsTestComputeProviderMetadata.java b/core/src/test/java/org/jclouds/providers/JcloudsTestComputeProviderMetadata.java
index 5a370e4..c7abbe4 100644
--- a/core/src/test/java/org/jclouds/providers/JcloudsTestComputeProviderMetadata.java
+++ b/core/src/test/java/org/jclouds/providers/JcloudsTestComputeProviderMetadata.java
@@ -19,7 +19,8 @@
 package org.jclouds.providers;
 
 import java.net.URI;
-import java.util.Set;
+
+import org.jclouds.apis.JcloudsTestComputeApiMetadata;
 
 import com.google.common.collect.ImmutableSet;
 
@@ -30,76 +31,36 @@
  */
 public class JcloudsTestComputeProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getId()}
-    */
-   @Override
-   public String getId() {
-      return "test-compute-provider";
+   public JcloudsTestComputeProviderMetadata() {
+      this(builder()
+            .api(new JcloudsTestComputeApiMetadata())
+            .id("test-compute-api")
+            .name("Test Compute Provider")
+            .homepage(URI.create("http://jclouds.org"))
+            .console(URI.create("http://jclouds.org/console"))
+            .iso3166Codes(ImmutableSet.of("US-VA", "US-CA")));
    }
 
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getType()}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected JcloudsTestComputeProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getName()}
-    */
-   @Override
-   public String getName() {
-      return "Test Compute Provider";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public JcloudsTestComputeProviderMetadata build() {
+         return new JcloudsTestComputeProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "user";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "password";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getHomepage()}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://jclouds.org");
-   }
-
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getConsole()}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("http://jclouds.org/console");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://jclouds.org/documentation");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-VA", "US-CA");
-   }
-
 }
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/providers/JcloudsTestYetAnotherComputeProviderMetadata.java b/core/src/test/java/org/jclouds/providers/JcloudsTestYetAnotherComputeProviderMetadata.java
index 433b1ea..3297b35 100644
--- a/core/src/test/java/org/jclouds/providers/JcloudsTestYetAnotherComputeProviderMetadata.java
+++ b/core/src/test/java/org/jclouds/providers/JcloudsTestYetAnotherComputeProviderMetadata.java
@@ -19,7 +19,8 @@
 package org.jclouds.providers;
 
 import java.net.URI;
-import java.util.Set;
+
+import org.jclouds.apis.JcloudsTestComputeApiMetadata;
 
 import com.google.common.collect.ImmutableSet;
 
@@ -30,76 +31,36 @@
  */
 public class JcloudsTestYetAnotherComputeProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getId()}
-    */
-   @Override
-   public String getId() {
-      return "test-yet-another-compute-provider";
+   public JcloudsTestYetAnotherComputeProviderMetadata() {
+      this(builder()
+            .api(new JcloudsTestComputeApiMetadata())
+            .id("test-yet-another-compute-provider")
+            .name("Test Yet Another Compute Provider")
+            .homepage(URI.create("http://jclouds.org"))
+            .console(URI.create("http://jclouds.org/console"))
+            .iso3166Codes(ImmutableSet.of("JP-13")));
    }
 
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getType()}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected JcloudsTestYetAnotherComputeProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getName()}
-    */
-   @Override
-   public String getName() {
-      return "Test Yet Another Compute Provider";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public JcloudsTestYetAnotherComputeProviderMetadata build() {
+         return new JcloudsTestYetAnotherComputeProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "user";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "password";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getHomepage()}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://jclouds.org");
-   }
-
-   /**
-    * {@ see org.jclouds.types.ProviderMetadata#getConsole()}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("http://jclouds.org/console");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://jclouds.org/documentation");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("JP-13");
-   }
-
 }
\ No newline at end of file
diff --git a/core/src/test/java/org/jclouds/providers/ProvidersTest.java b/core/src/test/java/org/jclouds/providers/ProvidersTest.java
index b3b84a3..0edf5d1 100644
--- a/core/src/test/java/org/jclouds/providers/ProvidersTest.java
+++ b/core/src/test/java/org/jclouds/providers/ProvidersTest.java
@@ -23,16 +23,17 @@
 
 import java.util.NoSuchElementException;
 
+import org.jclouds.apis.ApiType;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.Iterables;
 
 /**
  * The ProvidersTest tests the org.jclouds.providers.Providers class.
- *
+ * 
  * @author Jeremy Whitlock <jwhitlock@apache.org>
  */
-@Test( groups = "unit" )
+@Test(groups = "unit", testName = "ProvidersTest")
 public class ProvidersTest {
 
    private final ProviderMetadata testBlobstoreProvider = new JcloudsTestBlobStoreProviderMetadata();
@@ -42,11 +43,10 @@
    @Test
    public void testWithId() {
       ProviderMetadata providerMetadata;
-    
+
       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 exceptoin.");
       } catch (NoSuchElementException nsee) {
          ; // Expected
       }
@@ -58,6 +58,30 @@
 
    @Test
    public void testOfType() {
+      Iterable<ProviderMetadata> providersMetadata = Providers.ofType(ApiType.BLOBSTORE);
+
+      for (ProviderMetadata providerMetadata : providersMetadata) {
+         assertEquals(testBlobstoreProvider, providerMetadata);
+      }
+
+      providersMetadata = Providers.ofType(ApiType.COMPUTE);
+
+      for (ProviderMetadata providerMetadata : providersMetadata) {
+         if (providerMetadata.getName().equals(testComputeProvider.getName())) {
+            assertEquals(testComputeProvider, providerMetadata);
+         } else {
+            assertEquals(testYetAnotherComputeProvider, providerMetadata);
+         }
+      }
+
+      providersMetadata = Providers.ofType(ApiType.UNRECOGNIZED);
+
+      assertEquals(false, providersMetadata.iterator().hasNext());
+   }
+
+   @Test
+   @Deprecated
+   public void testOfTypeDeprecated() {
       Iterable<ProviderMetadata> providersMetadata = Providers.ofType(ProviderMetadata.BLOBSTORE_TYPE);
 
       for (ProviderMetadata providerMetadata : providersMetadata) {
@@ -86,10 +110,10 @@
       for (ProviderMetadata providerMetadata : providersMetadata) {
          if (providerMetadata.getName().equals(testBlobstoreProvider.getName())) {
             assertEquals(testBlobstoreProvider, providerMetadata);
-         } else if (providerMetadata.getName().equals(testComputeProvider.getName())){
+         } else if (providerMetadata.getName().equals(testComputeProvider.getName())) {
             assertEquals(testComputeProvider, providerMetadata);
          } else {
-             assertEquals(testYetAnotherComputeProvider, providerMetadata);
+            assertEquals(testYetAnotherComputeProvider, providerMetadata);
          }
       }
    }
@@ -105,6 +129,44 @@
       assertEquals(Iterables.size(Providers.boundedByIso3166Code("FAKE-CODE")), 0);
 
       // Test filtering by ISO 3166 code and type
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-CA", ApiType.BLOBSTORE)), 1);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-CA", ApiType.COMPUTE)), 1);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-FL", ApiType.BLOBSTORE)), 1);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-FL", ApiType.COMPUTE)), 0);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US", ApiType.BLOBSTORE)), 1);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US", ApiType.COMPUTE)), 1);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("FAKE-CODE", ApiType.BLOBSTORE)), 0);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("FAKE-CODE", ApiType.COMPUTE)), 0);
+   }
+
+   @Test
+   public void testCollocatedWith() {
+      // Test filtering by collocation alone
+      assertEquals(Iterables.size(Providers.collocatedWith(testBlobstoreProvider)), 1);
+      assertEquals(Iterables.size(Providers.collocatedWith(testComputeProvider)), 1);
+      assertEquals(Iterables.size(Providers.collocatedWith(testYetAnotherComputeProvider)), 0);
+
+      // Test filtering by collocation and type
+      assertEquals(Iterables.size(Providers.collocatedWith(testBlobstoreProvider, ApiType.BLOBSTORE)), 0);
+      assertEquals(Iterables.size(Providers.collocatedWith(testBlobstoreProvider, ApiType.COMPUTE)), 1);
+      assertEquals(Iterables.size(Providers.collocatedWith(testComputeProvider, ApiType.COMPUTE)), 0);
+      assertEquals(Iterables.size(Providers.collocatedWith(testComputeProvider, ApiType.BLOBSTORE)), 1);
+      assertEquals(Iterables.size(Providers.collocatedWith(testYetAnotherComputeProvider, ApiType.COMPUTE)), 0);
+      assertEquals(Iterables.size(Providers.collocatedWith(testYetAnotherComputeProvider, ApiType.BLOBSTORE)), 0);
+   }
+
+   @Test
+   @Deprecated
+   public void testBoundedByIso3166CodeDeprecated() {
+      // Test filtering by ISO 3166 code alone
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-CA")), 2);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-FL")), 1);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("US")), 2);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("JP-13")), 1);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("JP")), 1);
+      assertEquals(Iterables.size(Providers.boundedByIso3166Code("FAKE-CODE")), 0);
+
+      // Test filtering by ISO 3166 code and type
       assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-CA", ProviderMetadata.BLOBSTORE_TYPE)), 1);
       assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-CA", ProviderMetadata.COMPUTE_TYPE)), 1);
       assertEquals(Iterables.size(Providers.boundedByIso3166Code("US-FL", ProviderMetadata.BLOBSTORE_TYPE)), 1);
@@ -116,7 +178,8 @@
    }
 
    @Test
-   public void testCollocatedWith() {
+   @Deprecated
+   public void testCollocatedWithDeprecated() {
       // Test filtering by collocation alone
       assertEquals(Iterables.size(Providers.collocatedWith(testBlobstoreProvider)), 1);
       assertEquals(Iterables.size(Providers.collocatedWith(testComputeProvider)), 1);
@@ -127,9 +190,9 @@
       assertEquals(Iterables.size(Providers.collocatedWith(testBlobstoreProvider, ProviderMetadata.COMPUTE_TYPE)), 1);
       assertEquals(Iterables.size(Providers.collocatedWith(testComputeProvider, ProviderMetadata.COMPUTE_TYPE)), 0);
       assertEquals(Iterables.size(Providers.collocatedWith(testComputeProvider, ProviderMetadata.BLOBSTORE_TYPE)), 1);
-      assertEquals(Iterables.size(Providers.collocatedWith(testYetAnotherComputeProvider,
-            ProviderMetadata.COMPUTE_TYPE)), 0);
-      assertEquals(Iterables.size(Providers.collocatedWith(testYetAnotherComputeProvider,
-            ProviderMetadata.BLOBSTORE_TYPE)), 0);
+      assertEquals(
+            Iterables.size(Providers.collocatedWith(testYetAnotherComputeProvider, ProviderMetadata.COMPUTE_TYPE)), 0);
+      assertEquals(
+            Iterables.size(Providers.collocatedWith(testYetAnotherComputeProvider, ProviderMetadata.BLOBSTORE_TYPE)), 0);
    }
 }
diff --git a/core/src/test/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/core/src/test/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..5a98023
--- /dev/null
+++ b/core/src/test/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1,3 @@
+org.jclouds.apis.JcloudsTestBlobStoreApiMetadata
+org.jclouds.apis.JcloudsTestComputeApiMetadata
+org.jclouds.apis.JcloudsTestYetAnotherComputeApiMetadata
diff --git a/demos/getpath/src/test/java/org/jclouds/blobstore/ProvidersInPropertiesTest.java b/demos/getpath/src/test/java/org/jclouds/blobstore/ProvidersInPropertiesTest.java
deleted file mode 100644
index 732bad2..0000000
--- a/demos/getpath/src/test/java/org/jclouds/blobstore/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,43 +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.blobstore;
-
-import org.jclouds.blobstore.util.BlobStoreUtils;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedBlobStoreProviders() {
-      Iterable<String> providers = BlobStoreUtils.getSupportedProviders();
-      assert Iterables.contains(providers, "s3") : providers;
-      assert Iterables.contains(providers, "azureblob") : providers;
-      assert Iterables.contains(providers, "cloudfiles") : providers;
-
-   }
-
-}
diff --git a/demos/tweetstore/rhcloud-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java b/demos/tweetstore/rhcloud-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java
index 886a09b..5e3bd69 100644
--- a/demos/tweetstore/rhcloud-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java
+++ b/demos/tweetstore/rhcloud-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java
@@ -48,7 +48,7 @@
  * @author Andrew Phillips
  */
 public class PlatformServicesInitializer implements ServletContextListener {
-    public static final String PLATFORM_SERVICES_ATTRIBUTE_NAME = PlatformServicesInitializer.class.getName();
+    public static final String PLATFORM_SERVICES_ATTRIBUTE_NAME = PlatformServices.class.getName();
 
     // from .openshift/config/standalone.xml
     protected static final String HOST_VARIABLE = "OPENSHIFT_INTERNAL_IP";
diff --git a/demos/tweetstore/runatcloud-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java b/demos/tweetstore/runatcloud-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java
index c96bf64..bb02bbb 100644
--- a/demos/tweetstore/runatcloud-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java
+++ b/demos/tweetstore/runatcloud-tweetstore/src/main/java/org/jclouds/demo/paas/config/PlatformServicesInitializer.java
@@ -48,7 +48,7 @@
  * @author Andrew Phillips
  */
 public class PlatformServicesInitializer implements ServletContextListener {
-    public static final String PLATFORM_SERVICES_ATTRIBUTE_NAME = PlatformServicesInitializer.class.getName();
+    public static final String PLATFORM_SERVICES_ATTRIBUTE_NAME = PlatformServices.class.getName();
 
     // keep in sync with cloudbees-web.xml
     protected static final String HOST_PARAMETER = "application.host";
diff --git a/labs/aws-elb/src/main/java/org/jclouds/aws/elb/AWSELBProviderMetadata.java b/labs/aws-elb/src/main/java/org/jclouds/aws/elb/AWSELBProviderMetadata.java
index c916065..1c33e61 100644
--- a/labs/aws-elb/src/main/java/org/jclouds/aws/elb/AWSELBProviderMetadata.java
+++ b/labs/aws-elb/src/main/java/org/jclouds/aws/elb/AWSELBProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.aws.elb;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elb.ELBApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of @ link org.jclouds.types.ProviderMetadata} for Amazon's Elastic Load Balancing
@@ -34,82 +31,38 @@
  */
 public class AWSELBProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "aws-elb";
+   public AWSELBProviderMetadata() {
+      this(builder()
+            .id("aws-elb")
+            .name("Amazon Elastic Load Balancing")
+            .api(new ELBApiMetadata())
+            .homepage(URI.create("http://aws.amazon.com/elasticloadbalancing"))
+            .console(URI.create("https://console.aws.amazon.com/ec2/home"))
+            .linkedServices("aws-ec2","aws-elb", "aws-elb", "aws-s3", "aws-simpledb")
+            .iso3166Codes("US-VA", "US-CA", "BR-SP", "US-OR", "IE", "SG", "JP-13"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.LOADBALANCER_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AWSELBProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Amazon Elastic Load Balancing";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public AWSELBProviderMetadata build() {
+         return new AWSELBProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Access Key ID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret Access Key";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://aws.amazon.com/elasticloadbalancing");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://console.aws.amazon.com/ec2/home");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("aws-s3", "aws-ec2", "aws-elb", "aws-simpledb");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-VA", "US-CA", "US-OR", "BR-SP", "IE", "SG", "JP-13");
-   }
 }
\ No newline at end of file
diff --git a/labs/aws-elb/src/test/java/org/jclouds/aws/elb/AWSELBProviderTest.java b/labs/aws-elb/src/test/java/org/jclouds/aws/elb/AWSELBProviderTest.java
index f9b4403..7c56d55 100644
--- a/labs/aws-elb/src/test/java/org/jclouds/aws/elb/AWSELBProviderTest.java
+++ b/labs/aws-elb/src/test/java/org/jclouds/aws/elb/AWSELBProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.aws.elb;
 
+import org.jclouds.elb.ELBApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,6 +31,6 @@
 public class AWSELBProviderTest extends BaseProviderMetadataTest {
 
    public AWSELBProviderTest() {
-      super(new AWSELBProviderMetadata(), ProviderMetadata.LOADBALANCER_TYPE);
+      super(new AWSELBProviderMetadata(), new ELBApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/labs/elb/src/main/java/org/jclouds/elb/ELBApiMetadata.java b/labs/elb/src/main/java/org/jclouds/elb/ELBApiMetadata.java
new file mode 100644
index 0000000..f0397a4
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/ELBApiMetadata.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.elb;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Amazon's Elastic Load Balancing api.
+ * 
+ * @author Adrian Cole
+ */
+public class ELBApiMetadata extends BaseApiMetadata {
+
+   public ELBApiMetadata() {
+      this(builder()
+            .id("elb")
+            .type(ApiType.LOADBALANCER)
+            .name("Amazon Elastic Load Balancing Api")
+            .identityName("Access Key ID")
+            .credentialName("Secret Access Key")
+            .documentation(URI.create("http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected ELBApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public ELBApiMetadata build() {
+         return new ELBApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncClient.java b/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncClient.java
index 1d920e0..e8bd451 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncClient.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.elb;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -59,7 +58,6 @@
  */
 @Beta
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = ELBAsyncClient.VERSION)
 @VirtualHost
 public interface ELBAsyncClient {
    public static final String VERSION = "2011-11-15";
diff --git a/labs/elb/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/labs/elb/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..612af6e
--- /dev/null
+++ b/labs/elb/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.elb.ELBApiMetadata
\ No newline at end of file
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/labs/elb/src/test/java/org/jclouds/elb/ELBApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to labs/elb/src/test/java/org/jclouds/elb/ELBApiMetadataTest.java
index ace0f456..011d201 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/labs/elb/src/test/java/org/jclouds/elb/ELBApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.elb;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "ELBApiMetadataTest")
+public class ELBApiMetadataTest extends BaseApiMetadataTest {
 
+   public ELBApiMetadataTest() {
+      super(new ELBApiMetadata(), ApiType.LOADBALANCER);
+   }
 }
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 5964de9..bf3fe21 100644
--- a/labs/elb/src/test/java/org/jclouds/elb/ELBAsyncClientTest.java
+++ b/labs/elb/src/test/java/org/jclouds/elb/ELBAsyncClientTest.java
@@ -67,7 +67,7 @@
       assertNonPayloadHeadersEqual(request, "Host: elasticloadbalancing.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            String.format("Version=%s&Action=CreateLoadBalancer&Listeners.member.1.Protocol=http&LoadBalancerName=name&Listeners.member.1.LoadBalancerPort=80&Listeners.member.1.InstancePort=80", ELBAsyncClient.VERSION),
+            "Action=CreateLoadBalancer&Listeners.member.1.Protocol=http&LoadBalancerName=name&Listeners.member.1.LoadBalancerPort=80&Listeners.member.1.InstancePort=80",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -83,7 +83,7 @@
 
       assertRequestLineEquals(request, "POST https://elasticloadbalancing.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: elasticloadbalancing.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, String.format("Version=%s&Action=DescribeLoadBalancers", ELBAsyncClient.VERSION),
+      assertPayloadEquals(request, "Action=DescribeLoadBalancers",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -101,7 +101,7 @@
       assertNonPayloadHeadersEqual(request, "Host: elasticloadbalancing.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            String.format("Version=%s&Action=DescribeLoadBalancers&LoadBalancerNames.member.1=1&LoadBalancerNames.member.2=2", ELBAsyncClient.VERSION),
+            "Action=DescribeLoadBalancers&LoadBalancerNames.member.1=1&LoadBalancerNames.member.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -121,7 +121,7 @@
       assertNonPayloadHeadersEqual(request, "Host: elasticloadbalancing.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            String.format("Version=%s&Action=RegisterInstancesWithLoadBalancer&LoadBalancerName=ReferenceAP1&Instances.member.1.InstanceId=i-6055fa09", ELBAsyncClient.VERSION),
+            "Action=RegisterInstancesWithLoadBalancer&LoadBalancerName=ReferenceAP1&Instances.member.1.InstanceId=i-6055fa09",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -141,7 +141,7 @@
       assertNonPayloadHeadersEqual(request, "Host: elasticloadbalancing.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            String.format("Version=%s&Action=DeregisterInstancesFromLoadBalancer&LoadBalancerName=ReferenceAP1&Instances.member.1.InstanceId=i-6055fa09", ELBAsyncClient.VERSION),
+            "Action=DeregisterInstancesFromLoadBalancer&LoadBalancerName=ReferenceAP1&Instances.member.1.InstanceId=i-6055fa09",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/GleSYSApiMetadata.java b/labs/glesys/src/main/java/org/jclouds/glesys/GleSYSApiMetadata.java
new file mode 100644
index 0000000..be731d4
--- /dev/null
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/GleSYSApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for GleSYS API
+ * 
+ * @author Adrian Cole
+ */
+public class GleSYSApiMetadata extends BaseApiMetadata {
+
+   public GleSYSApiMetadata() {
+      this(builder()
+            .id("glesys")
+            .type(ApiType.COMPUTE)
+            .name("GleSYS API")
+            .identityName("Username")
+            .credentialName("API Key")
+            .documentation(URI.create("https://customer.glesys.com/api.php")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected GleSYSApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public GleSYSApiMetadata build() {
+         return new GleSYSApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/GleSYSProviderMetadata.java b/labs/glesys/src/main/java/org/jclouds/glesys/GleSYSProviderMetadata.java
index e8eb3b6..3d5da78 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/GleSYSProviderMetadata.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/GleSYSProviderMetadata.java
@@ -19,97 +19,47 @@
 package org.jclouds.glesys;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
- * Implementation of {@ link org.jclouds.types.ProviderMetadata} for GleSYS.
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for GleSYS.
  *
  * @author Adrian Cole
  */
 public class GleSYSProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "glesys";
+   public GleSYSProviderMetadata() {
+      this(builder()
+            .id("glesys")
+            .name("GleSYS")
+            .api(new GleSYSApiMetadata())
+            .homepage(URI.create("http://www.glesys.com"))
+            .console(URI.create("https://customer.glesys.com/cloud.php"))
+            .iso3166Codes("NL-NH","SE-N","US-NY","SE-AB"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected GleSYSProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "GleSYS";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public GleSYSProviderMetadata build() {
+         return new GleSYSProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Username";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "API Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.glesys.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://customer.glesys.com/cloud.php");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("https://customer.glesys.com/api.php");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("glesys");
-   }
-
-   /**
-   * {@inheritDoc}
-   */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("NL-NH","SE-N","US-NY","SE-AB");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/GleSYSProviderTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/GleSYSProviderTest.java
index 0b20c7a..b8a5751 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/GleSYSProviderTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/GleSYSProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.glesys;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +29,6 @@
 public class GleSYSProviderTest extends BaseProviderMetadataTest {
 
    public GleSYSProviderTest() {
-      super(new GleSYSProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new GleSYSProviderMetadata(), new GleSYSApiMetadata());
    }
 }
diff --git a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCApiMetadata.java b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCApiMetadata.java
new file mode 100644
index 0000000..718387b
--- /dev/null
+++ b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCApiMetadata.java
@@ -0,0 +1,50 @@
+package org.jclouds.savvis.vpdc;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the Savvis Symphony VPDC API
+ * 
+ * @author Adrian Cole
+ */
+public class SavvisSymphonyVPDCApiMetadata extends BaseApiMetadata {
+
+   public SavvisSymphonyVPDCApiMetadata() {
+      this(builder()
+            .id("savvis-symphonyvpdc")
+            .type(ApiType.COMPUTE)
+            .name("Savvis Symphony VPDC API")
+            .identityName("Username")
+            .credentialName("Password")
+            .documentation(URI.create("https://api.savvis.net/doc/spec/api/index.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SavvisSymphonyVPDCApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SavvisSymphonyVPDCApiMetadata build() {
+         return new SavvisSymphonyVPDCApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCProviderMetadata.java b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCProviderMetadata.java
index b651672..35319e9 100644
--- a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCProviderMetadata.java
+++ b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCProviderMetadata.java
@@ -18,13 +18,9 @@
  */
 package org.jclouds.savvis.vpdc;
 
-import com.google.common.collect.ImmutableSet;
-
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Savvis Symphony VPDC services.
@@ -32,110 +28,72 @@
  * @author Kedar Dave
  */
 public class SavvisSymphonyVPDCProviderMetadata extends BaseProviderMetadata {
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "savvis-symphonyvpdc";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Savvis Symphony VPDC";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "username";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "password";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.savvis.com/en-US/infrastructure-services/Cloud/Pages/SavvisSymphonyVPDC.aspx");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://www.savvisstation.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("https://api.savvis.net/doc/spec/api/index.html");
-   }
-
-   /**
+   public SavvisSymphonyVPDCProviderMetadata() {
+      this(builder()
+            .id("savvis-symphonyvpdc")
+            .name("Savvis Symphony VPDC")
+            .api(new SavvisSymphonyVPDCApiMetadata())
+            .homepage(URI.create("https://api.savvis.net/doc/spec/api/index.html"))
+            .console(URI.create("https://www.savvisstation.com"))
+               /**
     * {@inheritDoc}
     * list of data centers from savvisstation colocation guide 
     * https://www.savvisstation.com/DocumentViewer?GUID=a95f0387-cbfe-43eb-b25b-4f2b0f68498f&sessionid=SavvisCCC%3ac9a8984b9655b01916be587e5204b2cf
     * Once we have confirmation from Savvis as to what data centers are used for vpdc deployments,
     * iso codes for those will be entered here
     * 
-    * City								Code
-    * 	Lithia Springs, GA 				AT1
-	*	Waltham, MA 					BO1
-	*	Waltham, MA 					BO2
-	*	Waltham, MA 					B03
-	*	Elk Grove Village, IL 			CH3
-	*	Chicago, IL 					CH4 
-	*	Sterling, VA 					DC2
-	*	Sterling, VA 					DC3
-	*	Sterling, VA 					DC4 Phase I 
-	*   Sterling, VA 					DC4 Phase II
-	*	Fort Worth, TX					DL1 
-	*	Fort Worth, TX 					DL2 
-	*	El Segundo, CA 					LA1 
-	*	Jersey City, NJ 				NJ1
-	*	Weehawken, NJ 					NJ2
-	*	Piscataway, NJ 					NJ3
-	*	Piscataway, NJ 2nd floor 		NJ3
-	*	Weehawken, NJ 					NJ2X
-	*	Irvine, CA 						OC2
-	*	Santa Clara, CA 				SC4
-	*	Santa Clara, CA 				SC5
-	*	Santa Clara, CA 				SC8
-	*	Santa Clara, CA 				SC9
-	*	Tukwila, WA 1st floor 			SE2
-	*	Montreal, Canada 				MR1
-	*	Toronto, Canada 				TR1
-	*	Vancouver, Canada 				VC1
+    * City                       Code
+    *    Lithia Springs, GA            AT1
+   *  Waltham, MA                BO1
+   *  Waltham, MA                BO2
+   *  Waltham, MA                B03
+   *  Elk Grove Village, IL         CH3
+   *  Chicago, IL                CH4 
+   *  Sterling, VA               DC2
+   *  Sterling, VA               DC3
+   *  Sterling, VA               DC4 Phase I 
+   *   Sterling, VA              DC4 Phase II
+   *  Fort Worth, TX             DL1 
+   *  Fort Worth, TX                DL2 
+   *  El Segundo, CA                LA1 
+   *  Jersey City, NJ            NJ1
+   *  Weehawken, NJ              NJ2
+   *  Piscataway, NJ                NJ3
+   *  Piscataway, NJ 2nd floor      NJ3
+   *  Weehawken, NJ              NJ2X
+   *  Irvine, CA                 OC2
+   *  Santa Clara, CA            SC4
+   *  Santa Clara, CA            SC5
+   *  Santa Clara, CA            SC8
+   *  Santa Clara, CA            SC9
+   *  Tukwila, WA 1st floor         SE2
+   *  Montreal, Canada           MR1
+   *  Toronto, Canada            TR1
+   *  Vancouver, Canada             VC1
     */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("");
+            .iso3166Codes("US", "CA"));
    }
 
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SavvisSymphonyVPDCProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SavvisSymphonyVPDCProviderMetadata build() {
+         return new SavvisSymphonyVPDCProviderMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
 }
\ No newline at end of file
diff --git a/labs/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCProviderTest.java b/labs/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCProviderTest.java
index eac50a6..9bbca2f 100644
--- a/labs/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCProviderTest.java
+++ b/labs/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/SavvisSymphonyVPDCProviderTest.java
@@ -19,11 +19,11 @@
 package org.jclouds.savvis.vpdc;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
- * The SavvisSymphonyVPDCProviderTest tests the org.jclouds.savvis.vpdc.SavvisSymphonyVPDCProviderMetadata class.
+ * The SavvisSymphonyVPDCProviderTest tests the
+ * org.jclouds.savvis.vpdc.SavvisSymphonyVPDCProviderMetadata class.
  * 
  * @author Kedar Dave
  */
@@ -31,6 +31,6 @@
 public class SavvisSymphonyVPDCProviderTest extends BaseProviderMetadataTest {
 
    public SavvisSymphonyVPDCProviderTest() {
-      super(new SavvisSymphonyVPDCProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new SavvisSymphonyVPDCProviderMetadata(), new SavvisSymphonyVPDCApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorApiMetadata.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorApiMetadata.java
new file mode 100644
index 0000000..293275c
--- /dev/null
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorApiMetadata.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.vcloud.director.v1_5;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for VCloud Director 1.5 API
+ * 
+ * @author Adrian Cole
+ */
+public class VCloudDirectorApiMetadata extends BaseApiMetadata {
+
+   public VCloudDirectorApiMetadata() {
+      this(builder()
+            .id("vcloud-director")
+            .type(ApiType.COMPUTE)
+            .name("VCloud Director 1.5 API")
+            .identityName("User at Organization (user@org)")
+            .credentialName("Password")
+            .documentation(URI.create("http://www.vmware.com/support/pubs/vcd_pubs.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected VCloudDirectorApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public VCloudDirectorApiMetadata build() {
+         return new VCloudDirectorApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/CloneVAppParams.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/CloneVAppParams.java
index 4fdefb9..96b44d4 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/CloneVAppParams.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/CloneVAppParams.java
@@ -32,12 +32,13 @@
  */
 @XmlType(name = "CloneVAppParams")
 @XmlRootElement(name = "CloneVAppParams")
-public class CloneVAppParams extends InstantiateVAppParamsType {
+public class CloneVAppParams extends InstantiateVAppParams {
 
    public static Builder<?> builder() {
       return new ConcreteBuilder();
    }
 
+   @Override
    public Builder<?> toBuilder() {
       return builder().fromCloneVAppParams(this);
    }
@@ -45,7 +46,13 @@
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
    }
    
-   public static abstract class Builder<B extends Builder<B>> extends InstantiateVAppParamsType.Builder<B> {
+   public static abstract class Builder<B extends Builder<B>> extends InstantiateVAppParams.Builder<B> {
+
+      @SuppressWarnings("unchecked")
+      @Override
+      protected B self() {
+         return (B) this;
+      }
 
       @Override
       public CloneVAppParams build() {
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/CloneVAppTemplateParams.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/CloneVAppTemplateParams.java
index 1fa373c..aa4e4ac 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/CloneVAppTemplateParams.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/CloneVAppTemplateParams.java
@@ -28,17 +28,13 @@
 import javax.xml.bind.annotation.XmlType;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 
 /**
  * Represents parameters for copying a vApp template and optionally
  * deleting the source.
- * <p/>
- * <p/>
- * <p>Java class for CloneVAppTemplateParams complex type.
- * <p/>
- * <p>The following schema fragment specifies the expected content contained within this class.
- * <p/>
+ *
  * <pre>
  * &lt;complexType name="CloneVAppTemplateParams">
  *   &lt;complexContent>
@@ -63,6 +59,7 @@
       return new ConcreteBuilder();
    }
 
+   @Override
    public Builder<?> toBuilder() {
       return builder().fromCloneVAppTemplateParams(this);
    }
@@ -75,6 +72,12 @@
       private Reference source;
       private Boolean isSourceDelete;
 
+      @SuppressWarnings("unchecked")
+      @Override
+      protected B self() {
+         return (B) this;
+      }
+
       /**
        * @see CloneVAppTemplateParams#getSource()
        */
@@ -101,6 +104,7 @@
          return self();
       }
 
+      @Override
       public CloneVAppTemplateParams build() {
          return new CloneVAppTemplateParams(this);
       }
@@ -130,9 +134,6 @@
 
    /**
     * Gets the value of the source property.
-    *
-    * @return possible object is
-    *         {@link Reference }
     */
    public Reference getSource() {
       return source;
@@ -140,9 +141,6 @@
 
    /**
     * Gets the value of the isSourceDelete property.
-    *
-    * @return possible object is
-    *         {@link Boolean }
     */
    public Boolean isSourceDelete() {
       return isSourceDelete;
@@ -155,21 +153,21 @@
       if (o == null || getClass() != o.getClass())
          return false;
       CloneVAppTemplateParams that = CloneVAppTemplateParams.class.cast(o);
-      return equal(source, that.source) &&
-            equal(isSourceDelete, that.isSourceDelete);
+      return super.equals(that) &&
+            equal(this.source, that.source) &&
+            equal(this.isSourceDelete, that.isSourceDelete);
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(source,
-            isSourceDelete);
+      return Objects.hashCode(super.hashCode(), source, isSourceDelete);
    }
 
    @Override
-   public String toString() {
-      return Objects.toStringHelper("")
+   public ToStringHelper string() {
+      return super.string()
             .add("source", source)
-            .add("isSourceDelete", isSourceDelete).toString();
+            .add("isSourceDelete", isSourceDelete);
    }
 
 }
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppParams.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppParams.java
index 6f05baf..aeed48a 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppParams.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppParams.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,48 +16,176 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.vcloud.director.v1_5.domain;
 
+import static com.google.common.base.Objects.equal;
 
+import java.net.URI;
 
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
- * Parameters for Instantiating a vApp
+ * Represents vApp instantiation parameters.
  *
- * @author danikov
+ * @author grkvlt@apache.org
+ * @see <a href="http://www.vmware.com/support/vcd/doc/rest-api-doc-1.5-html/types/InstantiateVAppParamsType.html">
+ *    vCloud REST API - InstantiateVAppParamsType</a>
+ * @since 0.9
  */
-public class InstantiateVAppParams extends InstantiateVAppParamsType {
+@XmlRootElement(name = "InstantiateVAppParams")
+@XmlType(name = "InstantiateVAppParamsType")
+public class InstantiateVAppParams extends VAppCreationParamsType {
+
+   public static final String MEDIA_TYPe = VCloudDirectorMediaType.INSTANTIATE_VAPP_TEMPLATE_PARAMS;
 
    public static Builder<?> builder() {
       return new ConcreteBuilder();
    }
 
+   @Override
    public Builder<?> toBuilder() {
-      return builder().fromInstantiateVAppParams(this);
+      return builder().fromInstantiateVAppParamsType(this);
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
    }
    
-   public static abstract class Builder<B extends Builder<B>> extends InstantiateVAppParamsType.Builder<B> {
+   public static abstract class Builder<B extends Builder<B>> extends VAppCreationParamsType.Builder<B> {
+
+      private Reference source;
+      private Boolean sourceDelete;
+      private Boolean linkedClone;
+
+      @SuppressWarnings("unchecked")
+      @Override
+      protected B self() {
+         return (B) this;
+      }
+
+      /**
+       * @see InstantiateVAppParamsType#getSource()
+       */
+      public B source(Reference source) {
+         this.source = source;
+         return self();
+      }
+
+      /**
+       * Sets source to a new Reference that uses this URI as the href.
+       * 
+       * @see InstantiateVAppParamsType#getSource()
+       */
+      public B source(URI source) {
+         this.source = Reference.builder().href(source).build();
+         return self();
+      }
+
+      /**
+       * @see InstantiateVAppParamsType#isSourceDelete()
+       */
+      public B isSourceDelete(Boolean sourceDelete) {
+         this.sourceDelete = sourceDelete;
+         return self();
+      }
+
+      /**
+       * @see InstantiateVAppParamsType#isSourceDelete()
+       */
+      public B sourceDelete() {
+         this.sourceDelete = Boolean.TRUE;
+         return self();
+      }
+
+      /**
+       * @see InstantiateVAppParamsType#isSourceDelete()
+       */
+      public B notSourceDelete() {
+         this.sourceDelete = Boolean.FALSE;
+         return self();
+      }
+
+      /**
+       * @see InstantiateVAppParamsType#isLinkedClone()
+       */
+      public B isLinkedClone(Boolean linkedClone) {
+         this.linkedClone = linkedClone;
+         return self();
+      }
+
+      /**
+       * @see InstantiateVAppParamsType#isLinkedClone()
+       */
+      public B linkedClone() {
+         this.linkedClone = Boolean.TRUE;
+         return self();
+      }
+
+      /**
+       * @see InstantiateVAppParamsType#isLinkedClone()
+       */
+      public B notLinkedClone() {
+         this.linkedClone = Boolean.FALSE;
+         return self();
+      }
 
       @Override
       public InstantiateVAppParams build() {
          return new InstantiateVAppParams(this);
       }
 
-      public B fromInstantiateVAppParams(InstantiateVAppParams in) {
-         return fromInstantiateVAppParamsType(in);
+      public B fromInstantiateVAppParamsType(InstantiateVAppParams in) {
+         return fromVAppCreationParamsType(in)
+               .source(in.getSource())
+               .isSourceDelete(in.isSourceDelete())
+               .isLinkedClone(in.isLinkedClone());
       }
    }
+   
+   protected InstantiateVAppParams() {
+      // For JAXB and builder use
+   }
 
    public InstantiateVAppParams(Builder<?> builder) {
       super(builder);
+      this.source = builder.source;
+      this.sourceDelete = builder.sourceDelete;
+      this.linkedClone = builder.linkedClone;
    }
 
-   protected InstantiateVAppParams() {
-      // for JAXB
+   @XmlElement(name = "Source", required = true)
+   private Reference source;
+   @XmlElement(name = "IsSourceDelete")
+   private Boolean sourceDelete;
+   @XmlAttribute
+   private Boolean linkedClone;
+
+   /**
+    * Gets the value of the source property.
+    */
+   public Reference getSource() {
+      return source;
+   }
+
+   /**
+    * Gets the value of the isSourceDelete property.
+    */
+   public Boolean isSourceDelete() {
+      return sourceDelete;
+   }
+
+   /**
+    * Gets the value of the linkedClone property.
+    */
+   public Boolean isLinkedClone() {
+      return linkedClone;
    }
 
    @Override
@@ -67,6 +195,22 @@
       if (o == null || getClass() != o.getClass())
          return false;
       InstantiateVAppParams that = InstantiateVAppParams.class.cast(o);
-      return super.equals(that);
+      return super.equals(that) &&
+            equal(this.source, that.source) &&
+            equal(this.sourceDelete, that.sourceDelete) &&
+            equal(this.linkedClone, that.linkedClone);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), source, sourceDelete, linkedClone);
+   }
+
+   @Override
+   public ToStringHelper string() {
+      return super.string()
+            .add("source", source)
+            .add("isSourceDelete", sourceDelete)
+            .add("linkedClone", linkedClone);
    }
 }
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppParamsType.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppParamsType.java
deleted file mode 100644
index 95af149..0000000
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppParamsType.java
+++ /dev/null
@@ -1,204 +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.vcloud.director.v1_5.domain;
-
-import static com.google.common.base.Objects.equal;
-
-import java.net.URI;
-
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlType;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Objects.ToStringHelper;
-
-/**
- * Represents vApp instantiation parameters.
- *
- * <pre>
- * &lt;complexType name="InstantiateVAppParams" /&gt;
- * </pre>
- *
- * @author grkvlt@apache.org
- */
-@XmlType(name = "InstantiateVAppParams")
-public class InstantiateVAppParamsType extends VAppCreationParamsType {
-
-   public static Builder<?> builder() {
-      return new ConcreteBuilder();
-   }
-
-   public Builder<?> toBuilder() {
-      return builder().fromInstantiateVAppParamsType(this);
-   }
-
-   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
-   }
-   
-   public static abstract class Builder<B extends Builder<B>> extends VAppCreationParamsType.Builder<B> {
-
-      private Reference source;
-      private Boolean sourceDelete;
-      private Boolean linkedClone;
-
-      /**
-       * @see InstantiateVAppParamsType#getSource()
-       */
-      public B source(Reference source) {
-         this.source = source;
-         return self();
-      }
-
-      /**
-       * Sets source to a new Reference that uses this URI as the href.
-       * 
-       * @see InstantiateVAppParamsType#getSource()
-       */
-      public B source(URI source) {
-         this.source = Reference.builder().href(source).build();
-         return self();
-      }
-
-      /**
-       * @see InstantiateVAppParamsType#isSourceDelete()
-       */
-      public B isSourceDelete(Boolean sourceDelete) {
-         this.sourceDelete = sourceDelete;
-         return self();
-      }
-
-      /**
-       * @see InstantiateVAppParamsType#isSourceDelete()
-       */
-      public B sourceDelete() {
-         this.sourceDelete = Boolean.TRUE;
-         return self();
-      }
-
-      /**
-       * @see InstantiateVAppParamsType#isSourceDelete()
-       */
-      public B notSourceDelete() {
-         this.sourceDelete = Boolean.FALSE;
-         return self();
-      }
-
-      /**
-       * @see InstantiateVAppParamsType#isLinkedClone()
-       */
-      public B isLinkedClone(Boolean linkedClone) {
-         this.linkedClone = linkedClone;
-         return self();
-      }
-
-      /**
-       * @see InstantiateVAppParamsType#isLinkedClone()
-       */
-      public B linkedClone() {
-         this.linkedClone = Boolean.TRUE;
-         return self();
-      }
-
-      /**
-       * @see InstantiateVAppParamsType#isLinkedClone()
-       */
-      public B notLinkedClone() {
-         this.linkedClone = Boolean.FALSE;
-         return self();
-      }
-
-      @Override
-      public InstantiateVAppParamsType build() {
-         return new InstantiateVAppParamsType(this);
-      }
-
-      public B fromInstantiateVAppParamsType(InstantiateVAppParamsType in) {
-         return fromVAppCreationParamsType(in)
-               .source(in.getSource())
-               .isSourceDelete(in.isSourceDelete())
-               .isLinkedClone(in.isLinkedClone());
-      }
-   }
-   
-   protected InstantiateVAppParamsType() {
-      // For JAXB and builder use
-   }
-
-   public InstantiateVAppParamsType(Builder<?> builder) {
-      super(builder);
-      this.source = builder.source;
-      this.sourceDelete = builder.sourceDelete;
-      this.linkedClone = builder.linkedClone;
-   }
-
-   @XmlElement(name = "Source", required = true)
-   private Reference source;
-   @XmlElement(name = "IsSourceDelete")
-   private Boolean sourceDelete;
-   @XmlAttribute
-   private Boolean linkedClone;
-
-   /**
-    * Gets the value of the source property.
-    */
-   public Reference getSource() {
-      return source;
-   }
-
-   /**
-    * Gets the value of the isSourceDelete property.
-    */
-   public Boolean isSourceDelete() {
-      return sourceDelete;
-   }
-
-   /**
-    * Gets the value of the linkedClone property.
-    */
-   public Boolean isLinkedClone() {
-      return linkedClone;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o)
-         return true;
-      if (o == null || getClass() != o.getClass())
-         return false;
-      InstantiateVAppParamsType that = InstantiateVAppParamsType.class.cast(o);
-      return super.equals(that) &&
-            equal(this.source, that.source) &&
-            equal(this.sourceDelete, that.sourceDelete) &&
-            equal(this.linkedClone, that.linkedClone);
-   }
-
-   @Override
-   public int hashCode() {
-      return Objects.hashCode(super.hashCode(), source, sourceDelete, linkedClone);
-   }
-
-   @Override
-   public ToStringHelper string() {
-      return super.string()
-            .add("source", source)
-            .add("isSourceDelete", sourceDelete)
-            .add("linkedClone", linkedClone);
-   }
-}
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppTemplateParams.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppTemplateParams.java
index 73889ce..9d51566 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppTemplateParams.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiateVAppTemplateParams.java
@@ -34,7 +34,7 @@
  * </pre>
  */
 @XmlRootElement(name = "InstantiateVAppTemplateParams")
-public class InstantiateVAppTemplateParams extends InstantiateVAppParamsType {
+public class InstantiateVAppTemplateParams extends InstantiateVAppParams {
 
    public static Builder<?> builder() {
       return new ConcreteBuilder();
@@ -47,7 +47,7 @@
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
    }
    
-   public static abstract class Builder<B extends Builder<B>> extends InstantiateVAppParamsType.Builder<B> {
+   public static abstract class Builder<B extends Builder<B>> extends InstantiateVAppParams.Builder<B> {
 
       private Boolean allEULAsAccepted;
 
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiationParams.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiationParams.java
index c50176d..f9ef0e1 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiationParams.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/InstantiationParams.java
@@ -21,7 +21,6 @@
 import static com.google.common.base.Objects.equal;
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
 import java.util.Set;
 
 import javax.xml.bind.annotation.XmlElementRef;
@@ -29,19 +28,18 @@
 import javax.xml.bind.annotation.XmlType;
 
 import org.jclouds.vcloud.director.v1_5.domain.ovf.SectionType;
-import org.jclouds.vcloud.director.v1_5.domain.ovf.StartupSection;
 
 import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
 /**
  * Represents a list of ovf:Section to configure for instantiating a VApp.
  * 
- * <pre>
- * &lt;complexType name="InstantiationParams" /&gt;
- * </pre>
- * 
  * @author grkvlt@apache.org
+ * @see <a href="http://www.vmware.com/support/vcd/doc/rest-api-doc-1.5-html/types/InstantiationParamsType.html">
+ *    vCloud REST API - InstantiationParamsType</a>
+ * @since 0.9
  */
 @XmlRootElement(name = "InstantiationParams")
 @XmlType(name = "InstantiationParamsType")
@@ -56,13 +54,21 @@
    }
 
    public static class Builder {
-      private Set<? extends SectionType> sections = Sets.newLinkedHashSet();
+      private Set<SectionType> sections = Sets.newLinkedHashSet();
 
       /**
        * @see InstantiationParams#getSections()
        */
-      public Builder sections(Set<? extends SectionType> sections) {
-         this.sections = checkNotNull(sections, "sections");
+      public Builder sections(Iterable<? extends SectionType> sections) {
+         this.sections = Sets.newLinkedHashSet(checkNotNull(sections, "sections"));
+         return this;
+      }
+
+      /**
+       * @see InstantiationParams#getSections()
+       */
+      public Builder section(SectionType section) {
+         this.sections.add(checkNotNull(section, "section"));
          return this;
       }
 
@@ -81,7 +87,7 @@
    }
 
    private InstantiationParams(Set<? extends SectionType> sections) {
-      this.sections = sections;
+      this.sections = ImmutableSet.copyOf(sections);
    }
 
    @XmlElementRef
@@ -113,7 +119,7 @@
     * </ul>
     */
    public Set<? extends SectionType> getSections() {
-      return Collections.unmodifiableSet(this.sections);
+      return sections;
    }
 
    @Override
@@ -123,7 +129,7 @@
       if (o == null || getClass() != o.getClass())
          return false;
       InstantiationParams that = InstantiationParams.class.cast(o);
-      return equal(sections, that.sections);
+      return equal(this.sections, that.sections);
    }
 
    @Override
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/ParamsType.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/ParamsType.java
index 2822095..f90fb4b 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/ParamsType.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/ParamsType.java
@@ -30,11 +30,11 @@
 /**
  * A basic type used to specify parameters for operations.
  *
- * <pre>
- * &lt;complexType name="Params" /&gt;
- * </pre>
+ * @see <a href="http://www.vmware.com/support/vcd/doc/rest-api-doc-1.5-html/types/ParamsType.html">
+ *    vCloud REST API - ParamsType</a>
+ * @since 0.9
  */
-@XmlType(name = "Params")
+@XmlType(name = "ParamsType")
 public class ParamsType {
 
    public static Builder<?> builder() {
@@ -100,14 +100,14 @@
    protected String name;
 
    /**
-    * Gets the value of the description property.
+    * Optional description.
     */
    public String getDescription() {
       return description;
    }
 
    /**
-    * Gets the value of the name property.
+    * A name as parameter.
     */
    public String getName() {
       return name;
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/ResourceEntityType.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/ResourceEntityType.java
index 5924b86..d0ac707 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/ResourceEntityType.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/ResourceEntityType.java
@@ -127,6 +127,7 @@
       return new ConcreteBuilder();
    }
 
+   @Override
    public Builder<?> toBuilder() {
       return builder().fromResourceEntityType(this);
    }
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/UndeployVAppParams.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/UndeployVAppParams.java
index 65fb5f9..ced084e 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/UndeployVAppParams.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/UndeployVAppParams.java
@@ -20,26 +20,44 @@
 
 import static com.google.common.base.Objects.equal;
 
+import java.util.Arrays;
+import java.util.List;
+
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
 
+import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
+
 import com.google.common.base.Objects;
 
 /**
  * Represents vApp/VM undeployment parameters.
  * 
- * <pre>
- * &lt;complexType name="UndeployVAppParamsType" /&gt;
- * </pre>
- *
  * @author grkvlt@apache.org
+ * @see <a href="http://www.vmware.com/support/vcd/doc/rest-api-doc-1.5-html/types/UndeployVAppParamsType.html">
+ *    vCloud REST API - UndeployVAppParamsType</a>
  * @since 0.9
  */
 @XmlRootElement(name = "UndeployVAppParams")
 @XmlType(name = "UndeployVAppParamsType")
 public class UndeployVAppParams {
 
+   public static final String MEDIA_TYPe = VCloudDirectorMediaType.UNDEPLOY_VAPP_PARAMS;
+
+   public static class PowerAction {
+      /** Power off the VMs. This is the default action if this attribute is missing or empty) */
+      public static final String POWER_OFF = "powerOff";
+      /** Suspend the VMs. */
+      public static final String SUSPEND = "suspend";
+      /** Shut down the VMs. */
+      public static final String SHUTDOWN = "shutdown";
+      /** Attempt to power off the VMs. */
+      public static final String FORCE = "force";
+
+      public static final List<String> ALL = Arrays.asList(POWER_OFF, SUSPEND, SHUTDOWN, FORCE);
+   }
+
    public static Builder builder() {
       return new Builder();
    }
@@ -83,16 +101,14 @@
     *
     * All values other than {@code default} ignore actions, order, and delay specified in the StartupSection. One of:
     * <ul>
-    *    <li>{@code powerOff} (Power off the VMs. This is the default action if this attribute is missing or empty)
-    *    <li>{@code suspend} (Suspend the VMs)
-    *    <li>{@code shutdown} (Shut down the VMs)
-    *    <li>{@code force} (Attempt to power off the VMs.
+    *    <li>{@link PowerAction#POWER_OFF powerOff}
+    *    <li>{@link PowerAction#SUSPEND suspend}
+    *    <li>{@link PowerAction#SHUTDOWN shutdown}
+    *    <li>{@link PowerAction#FORCE force}
     * </ul>
     * Failures in undeploying the VM or associated networks are ignored. All references to the vApp and its VMs are
     * removed from the database), default (Use the actions, order, and delay specified in the StartupSection).
     *
-    * TODO add an enumeration for these values
-    *
     * @since 1.5
     */
    public String getUndeployPowerAction() {
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/VAppCreationParamsType.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/VAppCreationParamsType.java
index 365a9a3..5f25f98 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/VAppCreationParamsType.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/VAppCreationParamsType.java
@@ -30,11 +30,10 @@
 /**
  * Represents vApp creation parameters.
  *
- * <pre>
- * &lt;complexType name="VAppCreationParams" /&gt;
- * </pre>
- *
  * @author grkvlt@apache.org
+ * @see <a href="http://www.vmware.com/support/vcd/doc/rest-api-doc-1.5-html/types/VAppCreationParamsType.html">
+ *    vCloud REST API - VAppCreationParamsType</a>
+ * @since 0.9
  */
 @XmlType(name = "VAppCreationParamsType")
 public class VAppCreationParamsType extends ParamsType {
@@ -43,6 +42,7 @@
       return new ConcreteBuilder();
    }
 
+   @Override
    public Builder<?> toBuilder() {
       return builder().fromVAppCreationParamsType(this);
    }
@@ -148,28 +148,30 @@
    protected Boolean powerOn;
 
    /**
-    * Gets the value of the vAppParent property.
+    * Reserved.
+    *
+    * Unimplemented.
     */
    public Reference getVAppParent() {
       return vAppParent;
    }
 
    /**
-    * Gets the value of the instantiationParams property.
+    * Instantiation parameters of a VApp.
     */
    public InstantiationParams getInstantiationParams() {
       return instantiationParams;
    }
 
    /**
-    * Gets the value of the deploy property.
+    * Flag to deploy the VApp after successful creation.
     */
    public Boolean isDeploy() {
       return deploy;
    }
 
    /**
-    * Gets the value of the powerOn property.
+    * Flag to deploy and power on the VApp after successful creation.
     */
    public Boolean isPowerOn() {
       return powerOn;
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppAsyncClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppAsyncClient.java
index a9281a2..585f3fe 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppAsyncClient.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppAsyncClient.java
@@ -575,7 +575,7 @@
    @Consumes
    @JAXBResponseParser
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<ResourceAllocationSettingData> getVirtualHardwareSectionCpu(@EndpointParam URI vAppURI);
+   ListenableFuture<ResourceAllocationSettingData> getVirtualHardwareSectionCpu(@EndpointParam URI vmURI);
 
    /**
     * @see VAppClient#modifyVirtualHardwareSectionCpu(URI, ResourceAllocationSettingData)
@@ -585,7 +585,7 @@
    @Produces(OVF_RASD_ITEM)
    @Consumes(TASK)
    @JAXBResponseParser
-   ListenableFuture<Task> modifyVirtualHardwareSectionCpu(@EndpointParam URI vAppURI,
+   ListenableFuture<Task> modifyVirtualHardwareSectionCpu(@EndpointParam URI vmURI,
                                                           @BinderParam(BindToXMLPayload.class) ResourceAllocationSettingData rasd);
 
    /**
@@ -596,7 +596,7 @@
    @Consumes
    @JAXBResponseParser
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<RasdItemsList> getVirtualHardwareSectionDisks(@EndpointParam URI vAppURI);
+   ListenableFuture<RasdItemsList> getVirtualHardwareSectionDisks(@EndpointParam URI vmURI);
 
    /**
     * @see VAppClient#modifyVirtualHardwareSectionDisks(URI, RasdItemsList)
@@ -606,7 +606,7 @@
    @Produces(OVF_RASD_ITEMS_LIST)
    @Consumes(TASK)
    @JAXBResponseParser
-   ListenableFuture<Task> modifyVirtualHardwareSectionDisks(@EndpointParam URI vAppURI,
+   ListenableFuture<Task> modifyVirtualHardwareSectionDisks(@EndpointParam URI vmURI,
                                                             @BinderParam(BindToXMLPayload.class) RasdItemsList rasdItemsList);
 
    /**
@@ -617,7 +617,7 @@
    @Consumes
    @JAXBResponseParser
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<RasdItemsList> getVirtualHardwareSectionMedia(@EndpointParam URI vAppURI);
+   ListenableFuture<RasdItemsList> getVirtualHardwareSectionMedia(@EndpointParam URI vmURI);
 
    /**
     * @see VAppClient#getVirtualHardwareSectionMemory(URI)
@@ -627,7 +627,7 @@
    @Consumes
    @JAXBResponseParser
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<ResourceAllocationSettingData> getVirtualHardwareSectionMemory(@EndpointParam URI vAppURI);
+   ListenableFuture<ResourceAllocationSettingData> getVirtualHardwareSectionMemory(@EndpointParam URI vmURI);
 
    /**
     * @see VAppClient#modifyVirtualHardwareSectionMemory(URI, ResourceAllocationSettingData)
@@ -637,7 +637,7 @@
    @Produces(OVF_RASD_ITEM)
    @Consumes(TASK)
    @JAXBResponseParser
-   ListenableFuture<Task> modifyVirtualHardwareSectionMemory(@EndpointParam URI vAppURI,
+   ListenableFuture<Task> modifyVirtualHardwareSectionMemory(@EndpointParam URI vmURI,
                                                              @BinderParam(BindToXMLPayload.class) ResourceAllocationSettingData rasd);
 
    /**
@@ -648,7 +648,7 @@
    @Consumes
    @JAXBResponseParser
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<RasdItemsList> getVirtualHardwareSectionNetworkCards(@EndpointParam URI vAppURI);
+   ListenableFuture<RasdItemsList> getVirtualHardwareSectionNetworkCards(@EndpointParam URI vmURI);
 
    /**
     * @see VAppClient#modifyVirtualHardwareSectionNetworkCards(URI, RasdItemsList)
@@ -658,7 +658,7 @@
    @Produces(OVF_RASD_ITEMS_LIST)
    @Consumes(TASK)
    @JAXBResponseParser
-   ListenableFuture<Task> modifyVirtualHardwareSectionNetworkCards(@EndpointParam URI vAppURI,
+   ListenableFuture<Task> modifyVirtualHardwareSectionNetworkCards(@EndpointParam URI vmURI,
                                                                    @BinderParam(BindToXMLPayload.class) RasdItemsList rasdItemsList);
 
    /**
@@ -669,7 +669,7 @@
    @Consumes
    @JAXBResponseParser
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<RasdItemsList> getVirtualHardwareSectionSerialPorts(@EndpointParam URI vAppURI);
+   ListenableFuture<RasdItemsList> getVirtualHardwareSectionSerialPorts(@EndpointParam URI vmURI);
 
    /**
     * @see VAppClient#modifyVirtualHardwareSectionSerialPorts(URI, RasdItemsList)
@@ -679,7 +679,7 @@
    @Produces(OVF_RASD_ITEMS_LIST)
    @Consumes(TASK)
    @JAXBResponseParser
-   ListenableFuture<Task> modifyVirtualHardwareSectionSerialPorts(@EndpointParam URI vAppURI,
+   ListenableFuture<Task> modifyVirtualHardwareSectionSerialPorts(@EndpointParam URI vmURI,
                                                                   @BinderParam(BindToXMLPayload.class) RasdItemsList rasdItemsList);
 
    /**
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppClient.java
index eec10c4..2daa5e0 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppClient.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppClient.java
@@ -674,7 +674,7 @@
     *
     * @since 0.9
     */
-   ResourceAllocationSettingData getVirtualHardwareSectionCpu(URI vAppURI);
+   ResourceAllocationSettingData getVirtualHardwareSectionCpu(URI vmURI);
 
    /**
     * Modifies the CPU properties in virtual hardware section of a VM.
@@ -685,7 +685,7 @@
     *
     * @since 0.9
     */
-   Task modifyVirtualHardwareSectionCpu(URI vAppURI, ResourceAllocationSettingData rasd);
+   Task modifyVirtualHardwareSectionCpu(URI vmURI, ResourceAllocationSettingData rasd);
 
    /**
     * Retrieves a list of ResourceAllocationSettingData items for disks from virtual hardware section of a VM.
@@ -696,7 +696,7 @@
     *
     * @since 0.9
     */
-   RasdItemsList getVirtualHardwareSectionDisks(URI vAppURI);
+   RasdItemsList getVirtualHardwareSectionDisks(URI vmURI);
 
    /**
     * Modifies the disks list in virtual hardware section of a VM.
@@ -707,7 +707,7 @@
     *
     * @since 0.9
     */
-   Task modifyVirtualHardwareSectionDisks(URI vAppURI, RasdItemsList rasdItemsList);
+   Task modifyVirtualHardwareSectionDisks(URI vmURI, RasdItemsList rasdItemsList);
 
    /**
     * Retrieves the list of ResourceAllocationSettingData items that represents the floppies and CD/DVD drives in a VM.
@@ -718,7 +718,7 @@
     *
     * @since 0.9
     */
-   RasdItemsList getVirtualHardwareSectionMedia(URI vAppURI);
+   RasdItemsList getVirtualHardwareSectionMedia(URI vmURI);
 
    /**
     * Retrieves the ResourceAllocationSettingData item that contains memory information from virtual hardware section of a VM.
@@ -729,7 +729,7 @@
     *
     * @since 0.9
     */
-   ResourceAllocationSettingData getVirtualHardwareSectionMemory(URI vAppURI);
+   ResourceAllocationSettingData getVirtualHardwareSectionMemory(URI vmURI);
 
    /**
     * Modifies the memory properties in virtual hardware section of a VM.
@@ -740,7 +740,7 @@
     *
     * @since 0.9
     */
-   Task modifyVirtualHardwareSectionMemory(URI vAppURI, ResourceAllocationSettingData rasd);
+   Task modifyVirtualHardwareSectionMemory(URI vmURI, ResourceAllocationSettingData rasd);
 
    /**
     * Retrieves a list of ResourceAllocationSettingData items for network cards from virtual hardware section of a VM.
@@ -751,7 +751,7 @@
     *
     * @since 0.9
     */
-   RasdItemsList getVirtualHardwareSectionNetworkCards(URI vAppURI);
+   RasdItemsList getVirtualHardwareSectionNetworkCards(URI vmURI);
 
    /**
     * Modifies the network cards list in virtual hardware section of a VM.
@@ -762,7 +762,7 @@
     *
     * @since 0.9
     */
-   Task modifyVirtualHardwareSectionNetworkCards(URI vAppURI, RasdItemsList rasdItemsList);
+   Task modifyVirtualHardwareSectionNetworkCards(URI vmURI, RasdItemsList rasdItemsList);
 
    /**
     * Retrieves a list of ResourceAllocationSettingData items for serial ports from virtual hardware section of a VM.
@@ -773,7 +773,7 @@
     *
     * @since 1.5
     */
-   RasdItemsList getVirtualHardwareSectionSerialPorts(URI vAppURI);
+   RasdItemsList getVirtualHardwareSectionSerialPorts(URI vmURI);
 
    /**
     * Modifies the serial ports list in virtual hardware section of a VM.
@@ -784,7 +784,7 @@
     *
     * @since 1.5
     */
-   Task modifyVirtualHardwareSectionSerialPorts(URI vAppURI, RasdItemsList rasdItemsList);
+   Task modifyVirtualHardwareSectionSerialPorts(URI vmURI, RasdItemsList rasdItemsList);
 
    /**
     * @return synchronous access to {@link Metadata} features
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcAsyncClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcAsyncClient.java
index f8aafff..1a27f48 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcAsyncClient.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcAsyncClient.java
@@ -40,7 +40,7 @@
 import org.jclouds.vcloud.director.v1_5.domain.CloneVAppParams;
 import org.jclouds.vcloud.director.v1_5.domain.CloneVAppTemplateParams;
 import org.jclouds.vcloud.director.v1_5.domain.ComposeVAppParams;
-import org.jclouds.vcloud.director.v1_5.domain.InstantiateVAppParamsType;
+import org.jclouds.vcloud.director.v1_5.domain.InstantiateVAppParams;
 import org.jclouds.vcloud.director.v1_5.domain.Media;
 import org.jclouds.vcloud.director.v1_5.domain.UploadVAppTemplateParams;
 import org.jclouds.vcloud.director.v1_5.domain.VApp;
@@ -130,7 +130,7 @@
    @Produces(VCloudDirectorMediaType.INSTANTIATE_VAPP_TEMPLATE_PARAMS)
    @JAXBResponseParser
    ListenableFuture<VApp> instantiateVApp(@EndpointParam URI vdcURI,
-         @BinderParam(BindToXMLPayload.class) InstantiateVAppParamsType params);
+         @BinderParam(BindToXMLPayload.class) InstantiateVAppParams params);
    
    /**
     * @see VdcClient#uploadVAppTemplate(URI, UploadVAppTemplateParams)
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcClient.java
index 236eb30..d80c0ba 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcClient.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcClient.java
@@ -28,7 +28,7 @@
 import org.jclouds.vcloud.director.v1_5.domain.CloneVAppParams;
 import org.jclouds.vcloud.director.v1_5.domain.CloneVAppTemplateParams;
 import org.jclouds.vcloud.director.v1_5.domain.ComposeVAppParams;
-import org.jclouds.vcloud.director.v1_5.domain.InstantiateVAppParamsType;
+import org.jclouds.vcloud.director.v1_5.domain.InstantiateVAppParams;
 import org.jclouds.vcloud.director.v1_5.domain.Media;
 import org.jclouds.vcloud.director.v1_5.domain.UploadVAppTemplateParams;
 import org.jclouds.vcloud.director.v1_5.domain.VApp;
@@ -144,7 +144,7 @@
     * @return a VApp resource which will contain a task. The user should should wait for this task to finish to be able
     *         to use the vApp.
     */
-   VApp instantiateVApp(URI vdcUri, InstantiateVAppParamsType params);
+   VApp instantiateVApp(URI vdcUri, InstantiateVAppParams params);
    
    /**
     * Uploading vApp template to a vDC.
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/EntityPredicates.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/EntityPredicates.java
new file mode 100644
index 0000000..8d6f07e
--- /dev/null
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/EntityPredicates.java
@@ -0,0 +1,155 @@
+/*
+ * 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.vcloud.director.v1_5.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+
+import org.jclouds.vcloud.director.v1_5.domain.EntityType;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+/**
+ * Predicates for working with {@link EntityType} collections.
+ * 
+ * @author grkvlt@apache.org
+ */
+public class EntityPredicates {
+
+   /**
+    * Matches {@link EntityType entities} with the given name.
+    * 
+    * @param T type of the entity, for example {@link Vm}
+    * @param name value of the name attribute of the entity
+    * @return predicate that will match entities of the given name
+    */
+   public static <T extends EntityType> Predicate<T> nameEquals(final String name) {
+      checkNotNull(name, "name must be defined");
+
+      return new Predicate<T>() {
+         @Override
+         public boolean apply(T entity) {
+            return name.equals(entity.getName());
+         }
+
+         @Override
+         public String toString() {
+            return "nameEquals(" + name + ")";
+         }
+      };
+   }
+
+   /**
+    * Matches {@link EntityType entities} with names starting with the given prefix.
+    * 
+    * @param T type of the entity, for example {@link Vm}
+    * @param name prefix of the name attribute of the entity
+    * @return predicate that will match entities with names starting with the given prefix
+    */
+   public static <T extends EntityType> Predicate<T> nameStartsWith(final String prefix) {
+      checkNotNull(prefix, "prefix must be defined");
+
+      return new Predicate<T>() {
+         @Override
+         public boolean apply(T entity) {
+            String name = entity.getName();
+            return name != null && name.startsWith(prefix);
+         }
+
+         @Override
+         public String toString() {
+            return "nameStartsWith(" + prefix + ")";
+         }
+      };
+   }
+
+   /**
+    * Matches {@link EntityType entities} with names in the given collection.
+    *
+    * @param T type of the entity, for example {@link Vm}
+    * @param names collection of values for the name attribute of the entity
+    * @return predicate that will match entities with names starting with the given prefix
+    */
+   public static <T extends EntityType> Predicate<T> nameIn(final Iterable<String> names) {
+      checkNotNull(names, "names must be defined");
+
+      return new Predicate<T>() {
+         @Override
+         public boolean apply(T entity) {
+            String name = entity.getName();
+            return Iterables.contains(names, name);
+         }
+
+         @Override
+         public String toString() {
+            return "nameIn(" + Iterables.toString(names) + ")";
+         }
+      };
+   }
+
+   /**
+    * Matches {@link EntityType entities} of the given type.
+    * 
+    * @param T type of the entity, for example {@link Vm}
+    * @param type the media type string of the entity, for example {@link VCloudDirectorMediaType#CATALOG}
+    * @return predicate that will match entities of the given type
+    * @see VCloudDirectorMediaType
+    */
+   public static <T extends EntityType> Predicate<T> typeEquals(final String type) {
+      checkNotNull(type, "type must be defined");
+
+      return new Predicate<T>() {
+         @Override
+         public boolean apply(T entity) {
+            return type.equals(entity.getType());
+         }
+
+         @Override
+         public String toString() {
+            return "typeEquals(" + type + ")";
+         }
+      };
+   }
+
+   /**
+    * Matches {@link EntityType entities} with the given {@link URI}.
+    * 
+    * @param T type of the entity, for example {@link Vm}
+    * @param  href the URI of the entity
+    * @return predicate that will match entities with the given URI
+    * @see VCloudDirectorMediaType
+    */
+   public static <T extends EntityType> Predicate<T> hrefEquals(final URI href) {
+      checkNotNull(href, "href must be defined");
+
+      return new Predicate<T>() {
+         @Override
+         public boolean apply(T entity) {
+            return href.equals(entity.getHref());
+         }
+
+         @Override
+         public String toString() {
+            return "hrefEquals(" + href.toASCIIString() + ")";
+         }
+      };
+   }
+}
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/ReferencePredicates.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/ReferencePredicates.java
index e46dc36..c4bc049 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/ReferencePredicates.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/ReferencePredicates.java
@@ -20,7 +20,8 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
+import java.net.URI;
+
 import org.jclouds.vcloud.director.v1_5.domain.Reference;
 
 import com.google.common.base.Predicate;
@@ -127,4 +128,28 @@
          }
       };
    }
+
+   /**
+    * Matches {@link Reference}s with the given {@link URI}.
+    * 
+    * @param T type of the reference, for example {@link Link}
+    * @param  href the URI of the reference
+    * @return predicate that will match references with the given URI
+    * @see VCloudDirectorMediaType
+    */
+   public static <T extends Reference> Predicate<T> hrefEquals(final URI href) {
+      checkNotNull(href, "href must be defined");
+
+      return new Predicate<T>() {
+         @Override
+         public boolean apply(T reference) {
+            return href.equals(reference.getHref());
+         }
+
+         @Override
+         public String toString() {
+            return "hrefEquals(" + href.toASCIIString() + ")";
+         }
+      };
+   }
 }
diff --git a/labs/vcloud-director/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/labs/vcloud-director/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..262e987
--- /dev/null
+++ b/labs/vcloud-director/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.vcloud.director.v1_5.VCloudDirectorApiMetadata
\ No newline at end of file
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AbstractVAppClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/AbstractVAppClientLiveTest.java
similarity index 96%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AbstractVAppClientLiveTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/AbstractVAppClientLiveTest.java
index 6cb5114..0cdebc0 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AbstractVAppClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/AbstractVAppClientLiveTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5;
 
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.ENTITY_NON_NULL;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_FIELD_EQ;
@@ -49,6 +49,13 @@
 import org.jclouds.vcloud.director.v1_5.domain.cim.CimUnsignedInt;
 import org.jclouds.vcloud.director.v1_5.domain.cim.CimUnsignedLong;
 import org.jclouds.vcloud.director.v1_5.domain.cim.ResourceAllocationSettingData;
+import org.jclouds.vcloud.director.v1_5.features.CatalogClient;
+import org.jclouds.vcloud.director.v1_5.features.MetadataClient;
+import org.jclouds.vcloud.director.v1_5.features.QueryClient;
+import org.jclouds.vcloud.director.v1_5.features.VAppClient;
+import org.jclouds.vcloud.director.v1_5.features.VAppTemplateClient;
+import org.jclouds.vcloud.director.v1_5.features.VdcClient;
+import org.jclouds.vcloud.director.v1_5.features.MetadataClient.Writeable;
 import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorClientLiveTest;
 import org.jclouds.vcloud.director.v1_5.predicates.ReferencePredicates;
 import org.jclouds.xml.internal.JAXBParser;
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorApiMetadataTest.java
index ace0f456..5bd3bcd 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.vcloud.director.v1_5;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "VCloudDirectorApiMetadataTest")
+public class VCloudDirectorApiMetadataTest extends BaseApiMetadataTest {
 
+   public VCloudDirectorApiMetadataTest() {
+      super(new VCloudDirectorApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/CatalogClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/CatalogClientLiveTest.java
index ac1fa04..d52fc46 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/CatalogClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/CatalogClientLiveTest.java
@@ -87,29 +87,33 @@
       if (adminCatalog != null) return;
       catalogClient = context.getApi().getCatalogClient();
       Reference orgRef = Iterables.getFirst(context.getApi().getOrgClient().getOrgList().getOrgs(), null).toAdminReference(endpoint);
-
-      AdminCatalog newCatalog = AdminCatalog.builder()
-            .name(name("Test Catalog "))
-            .description("created by CatalogClientLiveTest")
-            .build();
       
-      AdminCatalogClient adminCatalogClient = adminContext.getApi().getCatalogClient();
-      adminCatalog = adminCatalogClient.createCatalog(orgRef.getHref(), newCatalog);
-      catalogRef = find(adminCatalog.getLinks(), and(relEquals("alternate"), typeEquals(VCloudDirectorMediaType.CATALOG)));
-
-      Metadata newMetadata = Metadata.builder()
-            .entry(MetadataEntry.builder().entry("KEY", "MARMALADE").build())
-            .build();
-
-      Task mergeCatalogMetadata = adminCatalogClient.getMetadataClient().mergeMetadata(adminCatalog.getHref(), newMetadata);
-      checkTask(mergeCatalogMetadata);
-      assertTrue(retryTaskSuccess.apply(mergeCatalogMetadata), String.format(TASK_COMPLETE_TIMELY, "setupRequiredClients"));
+      if (adminContext != null) {
+         AdminCatalog newCatalog = AdminCatalog.builder()
+               .name(name("Test Catalog "))
+               .description("created by CatalogClientLiveTest")
+               .build();
+         
+         AdminCatalogClient adminCatalogClient = adminContext.getApi().getCatalogClient();
+         adminCatalog = adminCatalogClient.createCatalog(orgRef.getHref(), newCatalog);
+         catalogRef = find(adminCatalog.getLinks(), and(relEquals("alternate"), typeEquals(VCloudDirectorMediaType.CATALOG)));
+   
+         Metadata newMetadata = Metadata.builder()
+               .entry(MetadataEntry.builder().entry("KEY", "MARMALADE").build())
+               .build();
+   
+         Task mergeCatalogMetadata = adminCatalogClient.getMetadataClient().mergeMetadata(adminCatalog.getHref(), newMetadata);
+         checkTask(mergeCatalogMetadata);
+         assertTrue(retryTaskSuccess.apply(mergeCatalogMetadata), String.format(TASK_COMPLETE_TIMELY, "setupRequiredClients"));
+      } else {
+         catalogRef = Reference.builder().href(catalogURI).build();
+      }
    }
    
    @AfterClass(alwaysRun = true)
    public void tearDown() {
       if (catalogItem != null)
-         catalogClient.deleteCatalogItem(catalogItem.getHref());               
+         catalogClient.deleteCatalogItem(catalogItem.getHref());
          
       if (media != null)
          context.getApi().getMediaClient().deleteMedia(media.getHref());
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientLiveTest.java
index 5cd6d27..43dad42 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientLiveTest.java
@@ -65,6 +65,7 @@
 import org.jclouds.vcloud.director.v1_5.domain.Task;
 import org.jclouds.vcloud.director.v1_5.domain.Vdc;
 import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorClientLiveTest;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -87,14 +88,7 @@
     */
    protected VdcClient vdcClient;
    protected MediaClient mediaClient;
-
-   @Override
-   @BeforeClass(alwaysRun = true)
-   public void setupRequiredClients() {
-      vdcClient = context.getApi().getVdcClient();
-      mediaClient = context.getApi().getMediaClient();
-   }
-
+   
    /*
     * Shared state between dependent tests.
     */
@@ -103,6 +97,23 @@
    private Metadata metadata;
    private MetadataValue metadataValue;
    private String metadataEntryValue = "value";
+
+   @Override
+   @BeforeClass(alwaysRun = true)
+   public void setupRequiredClients() {
+      vdcClient = context.getApi().getVdcClient();
+      mediaClient = context.getApi().getMediaClient();
+   }
+   
+   @AfterClass(alwaysRun = true)
+   protected void tidyUp() {
+      if (media != null) {
+         assertTaskSucceeds(mediaClient.deleteMedia(media.getHref()));
+      }
+      if (oldMedia != null) {
+         assertTaskSucceeds(mediaClient.deleteMedia(oldMedia.getHref()));
+      }
+   }
    
    @Test(description = "POST /vdc/{id}/media")
    public void testCreateMedia() throws URISyntaxException {
@@ -419,5 +430,8 @@
       
       deleteMedia = mediaClient.deleteMedia(oldMedia.getHref());
       Checks.checkTask(deleteMedia);
+      assertTrue(retryTaskSuccess.apply(deleteMedia),
+            String.format(TASK_COMPLETE_TIMELY, "deleteMedia"));
+      oldMedia = null;
    }
 }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/NetworkClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/NetworkClientLiveTest.java
index 80b697a..3b39a37 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/NetworkClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/NetworkClientLiveTest.java
@@ -57,19 +57,21 @@
     * Convenience reference to API client.
     */
    protected NetworkClient networkClient;
+   
+   private boolean metadataSet = false;
     
    @Override
    @BeforeClass(alwaysRun = true)
    public void setupRequiredClients() {
       networkClient = context.getApi().getNetworkClient();
-      adminContext.getApi().getNetworkClient().getMetadataClient().setMetadata(toAdminUri(networkURI), 
-            "key", MetadataValue.builder().value("value").build());
    }
    
-   @AfterClass(groups = { "live" })
+   @AfterClass(alwaysRun = true)
    public void cleanUp() throws Exception {
-      adminContext.getApi().getNetworkClient().getMetadataClient()
-      .deleteMetadataEntry(toAdminUri(networkURI), "key");
+      if (metadataSet) {
+         adminContext.getApi().getNetworkClient().getMetadataClient()
+            .deleteMetadataEntry(toAdminUri(networkURI), "key");
+      }
    }
    
    @Test(description = "GET /network/{id}")
@@ -87,8 +89,18 @@
       Checks.checkOrgNetwork(network);
    }
    
-   @Test(description = "GET /network/{id}/metadata")
+   private void setupMetadata() {
+      adminContext.getApi().getNetworkClient().getMetadataClient().setMetadata(toAdminUri(networkURI), 
+            "key", MetadataValue.builder().value("value").build());
+      metadataSet = true;
+   }
+   
+   @Test(description = "GET /network/{id}/metadata", dependsOnMethods = { "testGetNetwork" })
    public void testGetMetadata() {
+      if (adminContext != null) {
+         setupMetadata();
+      }
+      
       Metadata metadata = networkClient.getMetadataClient().getMetadata(networkURI);
       // required for testing
       assertFalse(Iterables.isEmpty(metadata.getMetadataEntries()), 
@@ -109,7 +121,7 @@
       }
    }
    
-   @Test(description = "GET /network/{id}/metadata/{key}")
+   @Test(description = "GET /network/{id}/metadata/{key}", dependsOnMethods = { "testGetMetadata" })
    public void testGetMetadataValue() {
       MetadataValue metadataValue = networkClient.getMetadataClient().getMetadataValue(networkURI, "key");
        
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/OrgClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/OrgClientLiveTest.java
index ced33aa..a709f4c 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/OrgClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/OrgClientLiveTest.java
@@ -70,7 +70,7 @@
    public void cleanUp() throws Exception {
       if (metadataSet) {
          adminContext.getApi().getOrgClient().getMetadataClient()
-            .deleteMetadataEntry(toAdminUri(orgURI), "key");
+            .deleteMetadataEntry(toAdminUri(orgURI), "KEY");
       }
    }
 
@@ -112,15 +112,18 @@
       checkOrg(org);
    }
    
-   @Test(description = "orgClient admin metadata setup", dependsOnMethods = { "testGetOrg" })
-   public void testSetupMetadata() {
+   private void setupMetadata() {
       adminContext.getApi().getOrgClient().getMetadataClient().setMetadata(toAdminUri(orgURI), 
             "KEY", MetadataValue.builder().value("VALUE").build()); 
       metadataSet = true;
    }
    
-   @Test(description = "GET /org/{id}/metadata", dependsOnMethods = { "testSetupMetadata" })
+   @Test(description = "GET /org/{id}/metadata", dependsOnMethods = { "testGetOrg" })
    public void testGetOrgMetadata() {
+      if (adminContext != null) {
+         setupMetadata();
+      }
+      
       // Call the method being tested
       Metadata metadata = orgClient.getMetadataClient().getMetadata(orgURI);
       
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientLiveTest.java
index c2bb9be..bfbe0a7 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientLiveTest.java
@@ -62,6 +62,7 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
+import org.jclouds.vcloud.director.v1_5.AbstractVAppClientLiveTest;
 import org.jclouds.vcloud.director.v1_5.VCloudDirectorException;
 import org.jclouds.vcloud.director.v1_5.domain.AccessSetting;
 import org.jclouds.vcloud.director.v1_5.domain.Checks;
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientLiveTest.java
index 1630dd3..1f1b2a9 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientLiveTest.java
@@ -41,6 +41,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.jclouds.vcloud.director.v1_5.AbstractVAppClientLiveTest;
 import org.jclouds.vcloud.director.v1_5.VCloudDirectorException;
 import org.jclouds.vcloud.director.v1_5.domain.Checks;
 import org.jclouds.vcloud.director.v1_5.domain.CloneVAppTemplateParams;
@@ -64,6 +65,7 @@
 import org.jclouds.vcloud.director.v1_5.domain.VAppTemplate;
 import org.jclouds.vcloud.director.v1_5.domain.ovf.Envelope;
 import org.jclouds.vcloud.director.v1_5.domain.ovf.NetworkSection;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Function;
@@ -80,6 +82,16 @@
 @Test(groups = { "live", "user", "vapptemplate" }, singleThreaded = true, testName = "VAppTemplateClientLiveTest")
 public class VAppTemplateClientLiveTest extends AbstractVAppClientLiveTest {
 
+   private String key;
+   private String val;
+   
+   @AfterClass(alwaysRun = true)
+   protected void tidyUp() {
+      if (key != null) {
+         assertTaskSucceeds(vAppTemplateClient.getMetadataClient().deleteMetadataEntry(vAppTemplateURI, key));
+      }
+   }
+
    // FIXME cloneVAppTemplate is giving back 500 error
    private VAppTemplate cloneVAppTemplate(boolean waitForTask) throws Exception {
       CloneVAppTemplateParams cloneVAppTemplateParams = CloneVAppTemplateParams.builder()
@@ -227,13 +239,11 @@
 
    @Test(description = "POST /vAppTemplate/{id}/metadata", dependsOnMethods = { "testGetVAppTemplate" })
    public void testEditMetadata() {
-      // TODO Cleanup after ourselves..
-      
       Metadata oldMetadata = vAppTemplateClient.getMetadataClient().getMetadata(vAppTemplateURI);
       Map<String,String> oldMetadataMap = metadataToMap(oldMetadata);
 
-      String key = name("key-");
-      String val = name("value-");
+      key = name("key-");
+      val = name("value-");
       MetadataEntry metadataEntry = MetadataEntry.builder().entry(key, val).build();
       Metadata metadata = Metadata.builder().fromMetadata(oldMetadata).entry(metadataEntry).build();
       
@@ -250,10 +260,7 @@
    
    @Test(description = "PUT /vAppTemplate/{id}/metadata/{key}", dependsOnMethods = { "testEditMetadata" })
    public void testEditMetadataValue() {
-      // TODO Cleanup after ourselves..
-      
-      String key = name("key-");
-      String val = name("value-");
+      val = "new"+val;
       MetadataValue metadataValue = MetadataValue.builder().value(val).build();
       
       final Task task = vAppTemplateClient.getMetadataClient().setMetadata(vAppTemplateURI, key, metadataValue);
@@ -265,19 +272,12 @@
 
    @Test(description = "DELETE /vAppTemplate/{id}/metadata/{key}", dependsOnMethods = { "testGetMetadataValue" })
    public void testDeleteVAppTemplateMetadataValue() {
-      // First store a value
-      String key = name("key-");
-      MetadataValue metadataValue = MetadataValue.builder().value("myval").build();
-      final Task task = vAppTemplateClient.getMetadataClient().setMetadata(vAppTemplateURI, key, metadataValue);
-      retryTaskSuccess.apply(task);
-      
-      // Then delete the entry
       final Task deletionTask = vAppTemplateClient.getMetadataClient().deleteMetadataEntry(vAppTemplateURI, key);
       retryTaskSuccess.apply(deletionTask);
 
-      // Then confirm the entry is not there
       Metadata newMetadata = vAppTemplateClient.getMetadataClient().getMetadata(vAppTemplateURI);
       checkMetadataKeyAbsentFor("vAppTemplate", newMetadata, key);
+      key = null;
    }
 
    @Test(description = "PUT /vAppTemplate/{id}/guestCustomizationSection")
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcClientLiveTest.java
index 5c90ae3..6724b66 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcClientLiveTest.java
@@ -83,6 +83,17 @@
    private VAppTemplate clonedVAppTemplate;
    private VAppTemplate capturedVAppTemplate;
    private VAppTemplate uploadedVAppTemplate;
+   private boolean metadataSet = false;
+   
+   @Override
+   @BeforeClass(alwaysRun = true)
+   public void setupRequiredClients() {
+      vdcClient = context.getApi().getVdcClient();
+      vappTemplateClient = context.getApi().getVAppTemplateClient();
+      vappClient = context.getApi().getVAppClient();
+      
+      assertNotNull(vdcURI, String.format(REF_REQ_LIVE, VDC));
+   }
    
    @AfterClass(alwaysRun = true)
    public void cleanUp() throws Exception {
@@ -105,18 +116,10 @@
          cleanUpVApp(composedVApp);
       }
       
-      adminContext.getApi().getVdcClient().getMetadataClient()
-         .deleteMetadataEntry(toAdminUri(vdcURI), "key");
-   }
-
-   @Override
-   @BeforeClass(alwaysRun = true)
-   public void setupRequiredClients() {
-      vdcClient = context.getApi().getVdcClient();
-      vappTemplateClient = context.getApi().getVAppTemplateClient();
-      vappClient = context.getApi().getVAppClient();
-      
-      assertNotNull(vdcURI, String.format(REF_REQ_LIVE, VDC));
+      if (metadataSet) {
+         adminContext.getApi().getVdcClient().getMetadataClient()
+            .deleteMetadataEntry(toAdminUri(vdcURI), "key");
+      }
    }
    
    @Test(description = "GET /vdc/{id}")
@@ -307,14 +310,18 @@
       
    }
    
-   @Test(description = "vdcClient admin metadata configuration", dependsOnMethods = { "testGetVdc" } )
-   public void testSetupMetadata() {
+   private void setupMetadata() {
       adminContext.getApi().getVdcClient().getMetadataClient().setMetadata(toAdminUri(vdcURI), 
             "key", MetadataValue.builder().value("value").build());
+      metadataSet = true;
    }
    
-   @Test(description = "GET /vdc/{id}/metadata", dependsOnMethods = { "testSetupMetadata" } )
+   @Test(description = "GET /vdc/{id}/metadata", dependsOnMethods = { "testGetVdc" } )
    public void testGetMetadata() {
+      if(adminContext != null) {
+         setupMetadata();
+      }
+      
       Metadata metadata = vdcClient.getMetadataClient().getMetadata(vdcURI);
       
       // required for testing
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientExpectTest.java
similarity index 99%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientExpectTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientExpectTest.java
index 02750dd..6391ce3 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientExpectTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.testng.Assert.assertEquals;
 
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientLiveTest.java
similarity index 94%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientLiveTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientLiveTest.java
index 8caf944..70d8b54 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminCatalogClientLiveTest.java
@@ -16,13 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static com.google.common.base.Objects.equal;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.NOT_NULL_OBJ_FMT;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_DEL;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_FIELD_EQ;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_FIELD_UPDATABLE;
+import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkError;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
@@ -79,6 +80,20 @@
       orgRef = Iterables.getFirst(context.getApi().getOrgClient().getOrgList().getOrgs(), null).toAdminReference(endpoint);
    }
    
+   @AfterClass(alwaysRun = true)
+   protected void tidyUp() {
+      if (catalog != null) {
+         catalogClient.deleteCatalog(catalog.getHref());
+         try {
+            catalogClient.getCatalog(catalog.getHref());
+            fail("The Catalog should have been deleted");
+         } catch (VCloudDirectorException vcde) {
+            checkError(vcde.getError());
+            assertEquals(vcde.getError().getMajorErrorCode(), Integer.valueOf(403), "The majorErrorCode should be 403 since the item has been deleted");
+         }
+      }
+   }
+   
    @Test(description = "POST /admin/org/{id}/catalogs")
    public void testCreateCatalog() {
       AdminCatalog newCatalog = AdminCatalog.builder()
@@ -224,11 +239,4 @@
          assertNull(deleteCatalog, String.format(OBJ_DEL, CATALOG, deleteCatalog.toString()));
       }
    }
-   
-   @AfterClass
-   protected void tidyUp() {
-      if (catalog != null) {
-         catalogClient.deleteCatalog(catalog.getHref());
-      }
-   }
 }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminNetworkClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientExpectTest.java
similarity index 98%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminNetworkClientExpectTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientExpectTest.java
index 3927872..5fe8699 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminNetworkClientExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientExpectTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.testng.Assert.assertEquals;
 
@@ -28,6 +28,7 @@
 import org.jclouds.vcloud.director.v1_5.domain.OrgNetwork;
 import org.jclouds.vcloud.director.v1_5.domain.Reference;
 import org.jclouds.vcloud.director.v1_5.domain.Task;
+import org.jclouds.vcloud.director.v1_5.features.NetworkClientExpectTest;
 import org.jclouds.vcloud.director.v1_5.features.admin.AdminNetworkClient;
 import org.jclouds.vcloud.director.v1_5.internal.VCloudDirectorAdminClientExpectTest;
 import org.testng.annotations.Test;
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminNetworkClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientLiveTest.java
similarity index 99%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminNetworkClientLiveTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientLiveTest.java
index ee152db..59f8eaf 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminNetworkClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminNetworkClientLiveTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static com.google.common.base.Objects.equal;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_FIELD_UPDATABLE;
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminOrgClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientExpectTest.java
similarity index 99%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminOrgClientExpectTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientExpectTest.java
index 088515d..11c725d 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminOrgClientExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientExpectTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.testng.Assert.assertEquals;
 
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminOrgClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientLiveTest.java
similarity index 99%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminOrgClientLiveTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientLiveTest.java
index 5a0fec8..9224390 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminOrgClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminOrgClientLiveTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static com.google.common.base.Objects.equal;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_FIELD_UPDATABLE;
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminQueryClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminQueryClientExpectTest.java
similarity index 98%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminQueryClientExpectTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminQueryClientExpectTest.java
index 23840cb..531c35a 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminQueryClientExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminQueryClientExpectTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.testng.Assert.assertEquals;
 
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminQueryClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminQueryClientLiveTest.java
similarity index 98%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminQueryClientLiveTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminQueryClientLiveTest.java
index 8455741..f6f8559 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminQueryClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminQueryClientLiveTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.testng.Assert.assertEquals;
 
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminVdcClientExpectTest.java
similarity index 94%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientExpectTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminVdcClientExpectTest.java
index 9de490f..59e962b 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminVdcClientExpectTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.testng.Assert.assertEquals;
 
@@ -26,6 +26,7 @@
 import org.jclouds.vcloud.director.v1_5.admin.VCloudDirectorAdminClient;
 import org.jclouds.vcloud.director.v1_5.domain.AdminVdc;
 import org.jclouds.vcloud.director.v1_5.domain.Reference;
+import org.jclouds.vcloud.director.v1_5.features.VdcClientExpectTest;
 import org.jclouds.vcloud.director.v1_5.features.admin.AdminVdcClient;
 import org.jclouds.vcloud.director.v1_5.internal.VCloudDirectorAdminClientExpectTest;
 import org.testng.annotations.Test;
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminVdcClientLiveTest.java
similarity index 96%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientLiveTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminVdcClientLiveTest.java
index 7f90c5b..baed95d 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminVdcClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/AdminVdcClientLiveTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_REQ_LIVE;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.REF_REQ_LIVE;
@@ -33,6 +33,9 @@
 import org.jclouds.vcloud.director.v1_5.domain.MetadataEntry;
 import org.jclouds.vcloud.director.v1_5.domain.MetadataValue;
 import org.jclouds.vcloud.director.v1_5.domain.Task;
+import org.jclouds.vcloud.director.v1_5.features.MetadataClient;
+import org.jclouds.vcloud.director.v1_5.features.VdcClient;
+import org.jclouds.vcloud.director.v1_5.features.MetadataClient.Writeable;
 import org.jclouds.vcloud.director.v1_5.features.admin.AdminVdcClient;
 import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorClientLiveTest;
 import org.testng.annotations.AfterClass;
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/GroupClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/GroupClientExpectTest.java
similarity index 98%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/GroupClientExpectTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/GroupClientExpectTest.java
index fa8dc98..0c7e470 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/GroupClientExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/GroupClientExpectTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.testng.Assert.assertEquals;
 
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/GroupClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/GroupClientLiveTest.java
similarity index 98%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/GroupClientLiveTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/GroupClientLiveTest.java
index a3f2593..8617396 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/GroupClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/GroupClientLiveTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static com.google.common.base.Objects.equal;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_FIELD_UPDATABLE;
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientExpectTest.java
similarity index 99%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientExpectTest.java
index b957599..77aa954 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientExpectTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static org.testng.Assert.assertEquals;
 
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientLiveTest.java
similarity index 98%
rename from labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java
rename to labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientLiveTest.java
index 4134697..93ad4f0 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/admin/UserClientLiveTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.vcloud.director.v1_5.features;
+package org.jclouds.vcloud.director.v1_5.features.admin;
 
 import static com.google.common.base.Objects.equal;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_FIELD_UPDATABLE;
@@ -217,7 +217,7 @@
          dependsOnMethods = { "testCreateUser" } )
    public void testDeleteUser() {
       // Create a user to be deleted (so we remove dependencies on test ordering)
-      User newUser = randomTestUser("testDeleteUser");
+      User newUser = randomTestUser("testDeleteUser"+getTestDateTimeStamp());
       User userToBeDeleted = userClient.createUser(orgRef.getHref(), newUser);
 
       // Delete the user
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java
index d08911c..d40b9cc 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.vcloud.director.v1_5.internal;
 
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.ENTITY_NON_NULL;
-import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.REF_REQ_LIVE;
 import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.TASK_COMPLETE_TIMELY;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -40,13 +39,9 @@
 import org.jclouds.compute.BaseVersionedServiceLiveTest;
 import org.jclouds.date.DateService;
 import org.jclouds.logging.Logger;
-import org.jclouds.logging.log4j.config.Log4JLoggingModule;
 import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.rest.RestContext;
-import org.jclouds.rest.RestContextFactory;
-import org.jclouds.sshj.config.SshjSshClientModule;
 import org.jclouds.vcloud.director.testng.FormatApiResultsListener;
-import org.jclouds.vcloud.director.v1_5.VCloudDirectorContext;
 import org.jclouds.vcloud.director.v1_5.VCloudDirectorException;
 import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
 import org.jclouds.vcloud.director.v1_5.admin.VCloudDirectorAdminAsyncClient;
@@ -79,8 +74,9 @@
 import org.jclouds.vcloud.director.v1_5.predicates.TaskSuccess;
 import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorAsyncClient;
 import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorClient;
-import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterSuite;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeSuite;
 import org.testng.annotations.Listeners;
 import org.testng.annotations.Test;
 
@@ -94,7 +90,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.inject.Guice;
-import com.google.inject.Module;
 
 /**
  * Tests behavior of {@link VCloudDirectorClient} and acts as parent for other client live tests.
@@ -127,6 +122,7 @@
    protected Session session;
 
    protected String catalogId;
+   protected URI catalogURI;
    protected URI vAppTemplateURI;
    protected URI mediaURI;
    protected URI networkURI;
@@ -142,6 +138,10 @@
 
    protected DateService dateService;
 
+   private static VCloudDirectorTestSession testSession;
+
+   private static String testStamp;
+
    @BeforeClass(alwaysRun = true)
    protected void setupDateService() {
       dateService = Guice.createInjector().getInstance(DateService.class);
@@ -160,53 +160,37 @@
    protected void initTaskSuccessLong(TaskSuccess taskSuccess) {
       retryTaskSuccessLong = new RetryablePredicate<Task>(taskSuccess, LONG_TASK_TIMEOUT_SECONDS * 1000L);
    }
+   
+   @BeforeSuite(alwaysRun = true)
+   protected void setupTestSession() {
+      setupCredentials();
+      Properties overrides = setupProperties();
+      testSession = VCloudDirectorTestSession.builder()
+         .identity(identity)
+         .credential(credential)
+         .provider(provider)
+         .overrides(overrides)
+         .endpoint(endpoint)
+         .build();
+   }
+   
+   @AfterSuite(alwaysRun = true)
+   protected void tearDownTestSession() {
+      testSession.close();
+   }
 
    @BeforeClass(alwaysRun = true)
    protected void setupContext() throws Exception {
       setupCredentials();
-      Properties overrides = setupProperties();
       
-      VCloudDirectorContext rootContext = VCloudDirectorContext.class.cast(
-            new RestContextFactory().createContext(provider, identity, credential, ImmutableSet.<Module> of(
-            new Log4JLoggingModule(), new SshjSshClientModule()), overrides));
-      adminContext = rootContext.getAdminContext();
+      context = testSession.getUserContext();
+      adminContext = testSession.getAdminContext();
       
-      rootContext.utils().injector().injectMembers(this);
-      Reference orgRef = Iterables.getFirst(rootContext.getApi().getOrgClient().getOrgList().getOrgs(), null)
-            .toAdminReference(endpoint);
-      assertNotNull(orgRef, String.format(REF_REQ_LIVE, "admin org"));
+      if(adminContext != null) {
+         adminSession = adminContext.getApi().getCurrentSession();
+         adminContext.utils().injector().injectMembers(this);
+      }
       
-      String adminIdentity = "testAdmin"+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
-      String adminCredential = "testAdminPassword";
-      rootContext.getAdminContext().getApi().getUserClient().createUser(orgRef.getHref(), User.builder()
-         .name(adminIdentity)
-         .password(adminCredential)
-         .description("test user with user-level privileges") //TODO desc
-         .role(getRoleReferenceFor(DefaultRoles.ORG_ADMIN))
-         .deployedVmQuota(REQUIRED_ADMIN_VM_QUOTA)
-         .isEnabled(true)
-         .build());
-      
-      rootContext.close(); rootContext = null;
-      
-      adminContext = VCloudDirectorContext.class.cast(new RestContextFactory().createContext(provider, adminIdentity, adminCredential, ImmutableSet.<Module> of(
-            new Log4JLoggingModule(), new SshjSshClientModule()), overrides)).getAdminContext();
-      adminSession = adminContext.getApi().getCurrentSession();
-      adminContext.utils().injector().injectMembers(this);
-      String userIdentity = "test"+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
-      String userCredential = "testPassword";
-      
-      adminContext.getApi().getUserClient().createUser(orgRef.getHref(), User.builder()
-         .name(userIdentity)
-         .password(userCredential)
-         .description("test user with user-level privileges")
-         .role(getRoleReferenceFor(DefaultRoles.USER))
-         .deployedVmQuota(REQUIRED_USER_VM_QUOTA)
-         .isEnabled(true)
-         .build());
-      
-      context = new RestContextFactory().createContext(provider, userIdentity, userCredential, ImmutableSet.<Module> of(
-               new Log4JLoggingModule(), new SshjSshClientModule()), overrides);
       session = context.getApi().getCurrentSession();
       context.utils().injector().injectMembers(this);
       
@@ -214,7 +198,19 @@
       setupRequiredClients();
    }
    
+   public static String getTestDateTimeStamp() {
+      if (testStamp == null) {
+         testStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+      }
+      
+      return testStamp;
+   }
+   
    public Reference getRoleReferenceFor(String name) {
+      return getRoleReferenceFor(name, adminContext);
+   }
+   
+   public static Reference getRoleReferenceFor(String name, RestContext<VCloudDirectorAdminClient, VCloudDirectorAdminAsyncClient> adminContext) {
       RoleReferences roles = adminContext.getApi().getQueryClient().roleReferencesQueryAll();
       // wrapped in a builder to strip out unwanted xml cruft that the api chokes on
       return Reference.builder().fromReference(Iterables.find(roles.getReferences(), ReferencePredicates.nameEquals(name))).build();
@@ -226,7 +222,7 @@
    
    public User randomTestUser(String prefix, Reference role) {
       return User.builder()
-         .name(name(prefix)+random.nextInt(999999))
+         .name(name(prefix)+getTestDateTimeStamp())
          .fullName("testFullName")
          .emailAddress("test@test.com")
          .telephone("555-1234")
@@ -245,6 +241,9 @@
    // TODO change properties to URI, not id
    protected void initTestParametersFromPropertiesOrLazyDiscover() {
       catalogId = Strings.emptyToNull(System.getProperty("test." + provider + ".catalog-id"));
+      if (catalogId != null) {
+         catalogURI = URI.create(endpoint + "/catalog/" + catalogId);
+      }
 
       String vAppTemplateId = Strings.emptyToNull(System.getProperty("test." + provider + ".vapptemplate-id"));
       if (vAppTemplateId != null)
@@ -287,14 +286,6 @@
          }
       }
    }
-
-   @AfterClass(alwaysRun = true)
-   protected void tearDown() {
-      if (context != null)
-         context.close();
-      if (adminContext != null)
-         adminContext.close();
-   }
    
    public URI toAdminUri(Reference ref) {
       return toAdminUri(ref.getHref());
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorTestSession.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorTestSession.java
new file mode 100644
index 0000000..940d18f
--- /dev/null
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/VCloudDirectorTestSession.java
@@ -0,0 +1,160 @@
+package org.jclouds.vcloud.director.v1_5.internal;
+
+import static com.google.common.base.Objects.equal;
+import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.REF_REQ_LIVE;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.logging.log4j.config.Log4JLoggingModule;
+import org.jclouds.rest.RestContext;
+import org.jclouds.rest.RestContextFactory;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.jclouds.vcloud.director.v1_5.VCloudDirectorContext;
+import org.jclouds.vcloud.director.v1_5.admin.VCloudDirectorAdminAsyncClient;
+import org.jclouds.vcloud.director.v1_5.admin.VCloudDirectorAdminClient;
+import org.jclouds.vcloud.director.v1_5.domain.Link;
+import org.jclouds.vcloud.director.v1_5.domain.Reference;
+import org.jclouds.vcloud.director.v1_5.domain.Role.DefaultRoles;
+import org.jclouds.vcloud.director.v1_5.domain.User;
+import org.jclouds.vcloud.director.v1_5.predicates.ReferencePredicates;
+import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorAsyncClient;
+import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorClient;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Module;
+
+public class VCloudDirectorTestSession {
+   
+   public static Builder builder() {
+      return new Builder();
+   }
+   
+   public static class Builder {
+      
+      private String provider;
+      private String identity;
+      private String credential;
+      private Properties overrides;
+      private String endpoint;
+
+      public Builder provider(String provider) {
+         this.provider = provider;
+         return this;
+      }
+      
+      public Builder identity(String identity) {
+         this.identity = identity;
+         return this;
+      }
+      
+      public Builder credential(String credential) {
+         this.credential = credential;
+         return this;
+      }
+      
+      public Builder overrides(Properties overrides) {
+         this.overrides = overrides;
+         return this;
+      }
+      
+      public Builder endpoint(String endpoint) {
+         this.endpoint = endpoint;
+         return this;
+      }
+      
+      public VCloudDirectorTestSession build() {
+         return new VCloudDirectorTestSession(provider, identity, credential, overrides, endpoint);
+      }
+   }
+   
+   private RestContext<VCloudDirectorAdminClient, VCloudDirectorAdminAsyncClient> adminContext;
+   private RestContext<VCloudDirectorClient, VCloudDirectorAsyncClient> userContext;
+
+   private User createdAdminUser;
+   private User createdUser;
+   
+   private VCloudDirectorTestSession(String provider, String identity, String credential, Properties overrides, String endpoint) {
+      VCloudDirectorContext rootContext = VCloudDirectorContext.class.cast(
+            new RestContextFactory().createContext(provider, identity, credential, ImmutableSet.<Module> of(
+            new Log4JLoggingModule(), new SshjSshClientModule()), overrides));
+      
+      if (rootContext.getApi().getCurrentSession().getLinks().contains(Link.builder()
+         .rel("down")
+         .type("application/vnd.vmware.admin.vcloud+xml")
+         .href(URI.create(endpoint+"/admin/"))
+         .build())) {
+         
+         adminContext = rootContext.getAdminContext();
+         
+         Reference orgRef = Iterables.getFirst(rootContext.getApi().getOrgClient().getOrgList().getOrgs(), null)
+               .toAdminReference(endpoint);
+         assertNotNull(orgRef, String.format(REF_REQ_LIVE, "admin org"));
+         
+         Reference userRef = Iterables.find(adminContext.getApi().getOrgClient().getOrg(orgRef.getHref()).getUsers(), 
+               ReferencePredicates.nameEquals(adminContext.getApi().getCurrentSession().getUser()));
+         
+         User user = adminContext.getApi().getUserClient().getUser(userRef.getHref());
+         Reference orgAdmin = user.getRole();
+         assertTrue(equal(orgAdmin.getName(), DefaultRoles.ORG_ADMIN), "must give org admin or user-only credentials");
+         
+         String adminIdentity = "testAdmin"+BaseVCloudDirectorClientLiveTest.getTestDateTimeStamp();
+         String adminCredential = "testAdminPassword";
+         
+         createdAdminUser = rootContext.getAdminContext().getApi().getUserClient().createUser(orgRef.getHref(), User.builder()
+            .name(adminIdentity)
+            .password(adminCredential)
+            .description("test user with user-level privileges")
+            .role(orgAdmin)
+            .deployedVmQuota(BaseVCloudDirectorClientLiveTest.REQUIRED_ADMIN_VM_QUOTA)
+            .isEnabled(true)
+            .build());
+         
+         rootContext.close(); rootContext = null;
+         
+         adminContext = VCloudDirectorContext.class.cast(new RestContextFactory().createContext(provider, adminIdentity, adminCredential, ImmutableSet.<Module> of(
+               new Log4JLoggingModule(), new SshjSshClientModule()), overrides)).getAdminContext();
+         String userIdentity = "test"+BaseVCloudDirectorClientLiveTest.getTestDateTimeStamp();
+         String userCredential = "testPassword";
+         
+         createdUser = adminContext.getApi().getUserClient().createUser(orgRef.getHref(), User.builder()
+            .name(userIdentity)
+            .password(userCredential)
+            .description("test user with user-level privileges")
+            .role(BaseVCloudDirectorClientLiveTest.getRoleReferenceFor(DefaultRoles.USER, adminContext))
+            .deployedVmQuota(BaseVCloudDirectorClientLiveTest.REQUIRED_USER_VM_QUOTA)
+            .isEnabled(true)
+            .build());
+         
+         userContext = new RestContextFactory().createContext(provider, userIdentity, userCredential, ImmutableSet.<Module> of(
+                  new Log4JLoggingModule(), new SshjSshClientModule()), overrides);
+      } else {
+         userContext = rootContext;
+      }
+   }
+   
+   public void close() {
+      if (createdUser != null) {
+         adminContext.getApi().getUserClient().deleteUser(createdUser.getHref());
+      }
+      if (userContext != null)
+         userContext.close();
+      if (createdAdminUser != null) {
+         // TODO: may have to preserve root context if we can't delete the user for it's own context here
+         adminContext.getApi().getUserClient().deleteUser(createdAdminUser.getHref()); 
+      }
+      if (adminContext != null)
+         adminContext.close();
+   }
+   
+   public RestContext<VCloudDirectorClient, VCloudDirectorAsyncClient> getUserContext() {
+      return userContext;
+   }
+   
+   public RestContext<VCloudDirectorAdminClient, VCloudDirectorAdminAsyncClient> getAdminContext() {
+      return adminContext;
+   }
+}
diff --git a/labs/virtualbox/README.md b/labs/virtualbox/README.md
index cbf2589..9b7e26d 100644
--- a/labs/virtualbox/README.md
+++ b/labs/virtualbox/README.md
@@ -3,7 +3,7 @@
 
 Have virtualbox 4.1.8 installed. 
 
-Make sure you change your VirtualBox preferences to not auto-capture keyboard, and also set host key to none. Otherwise you may accidentally screw-up automated installs.
+Have an ssh daemon with passwordless login to localhost (i.e. "ssh [me]@localhost" must work without password).
 
 That's it! 
 
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java
new file mode 100644
index 0000000..5c7a800
--- /dev/null
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.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.virtualbox;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for VirtualBox API
+ * 
+ * @author Adrian Cole
+ */
+public class VirtualBoxApiMetadata extends BaseApiMetadata {
+
+   public VirtualBoxApiMetadata() {
+      this(builder()
+            .id("virtualbox")
+            .type(ApiType.COMPUTE)
+            .name("VirtualBox API")
+            .identityName("User")
+            .credentialName("Password")
+            .documentation(URI.create("https://www.virtualbox.org/sdkref/index.html")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected VirtualBoxApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public VirtualBoxApiMetadata build() {
+         return new VirtualBoxApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java
index b54e203..c6852e5 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java
@@ -19,7 +19,9 @@
 
 package org.jclouds.virtualbox.config;
 
-import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_ARCH;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_OS;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_VERSION;
 
 import java.io.File;
 import java.io.InputStream;
@@ -29,7 +31,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
-import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.eclipse.jetty.server.Server;
@@ -46,7 +47,6 @@
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeState;
-import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.TemplateBuilder;
 import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
 import org.jclouds.domain.Location;
@@ -56,7 +56,6 @@
 import org.jclouds.ssh.SshClient;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.jclouds.virtualbox.Host;
-import org.jclouds.virtualbox.Preconfiguration;
 import org.jclouds.virtualbox.compute.VirtualBoxComputeServiceAdapter;
 import org.jclouds.virtualbox.domain.CloneSpec;
 import org.jclouds.virtualbox.domain.ExecutionType;
@@ -76,7 +75,7 @@
 import org.jclouds.virtualbox.functions.YamlImagesFromFileConfig;
 import org.jclouds.virtualbox.functions.admin.FileDownloadFromURI;
 import org.jclouds.virtualbox.functions.admin.ImagesToYamlImagesFromYamlDescriptor;
-import org.jclouds.virtualbox.functions.admin.StartJettyIfNotAlreadyRunning;
+import org.jclouds.virtualbox.functions.admin.PreseedCfgServer;
 import org.jclouds.virtualbox.functions.admin.StartVBoxIfNotAlreadyRunning;
 import org.jclouds.virtualbox.predicates.SshResponds;
 import org.testng.internal.annotations.Sets;
@@ -91,7 +90,6 @@
 import com.google.common.base.Predicate;
 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;
@@ -128,10 +126,10 @@
       bind(new TypeLiteral<Function<IMachine, Image>>() {
       }).to(IMachineToImage.class);
       bind(new TypeLiteral<CacheLoader<IsoSpec, URI>>() {
-      }).to((Class) StartJettyIfNotAlreadyRunning.class);
+      }).to((Class) PreseedCfgServer.class);
       bind(new TypeLiteral<Function<URI, File>>() {
       }).to((Class) FileDownloadFromURI.class);
-      
+
       bind(new TypeLiteral<Supplier<VirtualBoxManager>>() {
       }).to((Class) StartVBoxIfNotAlreadyRunning.class);
       // the yaml config to image mapper
@@ -139,9 +137,11 @@
       }).to((Class) ImagesToYamlImagesFromYamlDescriptor.class);
       // the yaml config provider
       bind(YamlImagesFromFileConfig.class);
+
       // the master machines cache
       bind(new TypeLiteral<LoadingCache<Image, Master>>() {
-      }).to((Class) MastersLoadingCache.class);
+      }).to(MastersLoadingCache.class);
+
       // the master creating function
       bind(new TypeLiteral<Function<MasterSpec, IMachine>>() {
       }).to((Class) CreateAndInstallVm.class);
@@ -150,6 +150,9 @@
       }).to((Class) NodeCreator.class);
       bind(new TypeLiteral<Function<CloneSpec, IMachine>>() {
       }).to((Class) CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.class);
+      // the jetty server provider
+      bind(new TypeLiteral<Server>() {
+      }).to((Class) PreseedCfgServer.class).asEagerSingleton();
 
       // for byon
       bind(new TypeLiteral<Function<URI, InputStream>>() {
@@ -163,13 +166,6 @@
    }
 
    @Provides
-   @Singleton
-   @Preconfiguration
-   protected LoadingCache<IsoSpec, URI> preconfiguration(CacheLoader<IsoSpec, URI> cacheLoader) {
-      return CacheBuilder.newBuilder().build(cacheLoader);
-   }
-
-   @Provides
    @Host
    @Singleton
    protected ComputeServiceContext provideHostController() {
@@ -182,12 +178,6 @@
 
    @Provides
    @Singleton
-   protected Server providesJettyServer(@Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) {
-      return new Server(URI.create(preconfigurationUrl).getPort());
-   }
-
-   @Provides
-   @Singleton
    protected Function<Supplier<NodeMetadata>, VirtualBoxManager> provideVBox() {
       return new Function<Supplier<NodeMetadata>, VirtualBoxManager>() {
 
@@ -230,7 +220,8 @@
 
    @Override
    protected TemplateBuilder provideTemplate(Injector injector, TemplateBuilder template) {
-      return template.osFamily(OsFamily.UBUNTU).osVersionMatches("11.04");
+      return template.osFamily(VIRTUALBOX_DEFAULT_IMAGE_OS).osVersionMatches(VIRTUALBOX_DEFAULT_IMAGE_VERSION)
+               .osArchMatches(VIRTUALBOX_DEFAULT_IMAGE_ARCH);
    }
 
    @Provides
@@ -248,8 +239,7 @@
 
    @VisibleForTesting
    public static final Map<MachineState, NodeState> machineToNodeState = ImmutableMap
-            .<MachineState, NodeState> builder()
-            .put(MachineState.Running, NodeState.RUNNING)
+            .<MachineState, NodeState> builder().put(MachineState.Running, NodeState.RUNNING)
             .put(MachineState.PoweredOff, NodeState.SUSPENDED)
             .put(MachineState.DeletingSnapshot, NodeState.PENDING)
             .put(MachineState.DeletingSnapshotOnline, NodeState.PENDING)
@@ -261,15 +251,10 @@
             .put(MachineState.Stopping, NodeState.PENDING)
             .put(MachineState.Restoring, NodeState.PENDING)
             // TODO What to map these states to?
-            .put(MachineState.FirstOnline, NodeState.PENDING)
-            .put(MachineState.FirstTransient, NodeState.PENDING)
-            .put(MachineState.LastOnline, NodeState.PENDING)
-            .put(MachineState.LastTransient, NodeState.PENDING)
-            .put(MachineState.Teleported, NodeState.PENDING)
-            .put(MachineState.TeleportingIn, NodeState.PENDING)
-            .put(MachineState.TeleportingPausedVM, NodeState.PENDING)
-            .put(MachineState.Aborted, NodeState.ERROR)
-            .put(MachineState.Stuck, NodeState.ERROR)
-            .put(MachineState.Null, NodeState.UNRECOGNIZED).build();
+            .put(MachineState.FirstOnline, NodeState.PENDING).put(MachineState.FirstTransient, NodeState.PENDING)
+            .put(MachineState.LastOnline, NodeState.PENDING).put(MachineState.LastTransient, NodeState.PENDING)
+            .put(MachineState.Teleported, NodeState.PENDING).put(MachineState.TeleportingIn, NodeState.PENDING)
+            .put(MachineState.TeleportingPausedVM, NodeState.PENDING).put(MachineState.Aborted, NodeState.ERROR)
+            .put(MachineState.Stuck, NodeState.ERROR).put(MachineState.Null, NodeState.UNRECOGNIZED).build();
 
 }
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java
index 9092976..a96fff3 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java
@@ -21,6 +21,8 @@
 
 import java.io.File;
 
+import org.jclouds.compute.domain.OsFamily;
+
 /**
  * Configuration properties used for interacting with VirtualBox instances.
  * 
@@ -30,7 +32,7 @@
 public interface VirtualBoxConstants {
 
    public static final String VIRTUALBOX_NODE_NAME_SEPARATOR = "-0x0-";
-   
+
    public static final String VIRTUALBOX_IMAGE_PREFIX = "jclouds-image" + VIRTUALBOX_NODE_NAME_SEPARATOR;
 
    public static final String VIRTUALBOX_NODE_PREFIX = "jclouds-node" + VIRTUALBOX_NODE_NAME_SEPARATOR;
@@ -60,6 +62,12 @@
    public static final String VIRTUALBOX_DEFAULT_DIR = System.getProperty("user.home") + File.separator
             + ".jclouds-vbox";
 
+   public static final OsFamily VIRTUALBOX_DEFAULT_IMAGE_OS = OsFamily.UBUNTU;
+   
+   public static final String VIRTUALBOX_DEFAULT_IMAGE_VERSION = "11.10";
+   
+   public static final String VIRTUALBOX_DEFAULT_IMAGE_ARCH = "x86";
+
    public static final String VIRTUALBOX_PROVIDER = "virtualbox";
 
 }
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java
index af4aac4..37dbb7f 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java
@@ -139,7 +139,7 @@
          OsFamily family = parseOsFamilyOrUnrecognized(arg0.os_family);
 
          OperatingSystem operatingSystem = OperatingSystem.builder().description(arg0.os_description).family(family)
-                  .version(arg0.os_version).is64Bit(arg0.os_64bit).build();
+                  .version(arg0.os_version).is64Bit(arg0.os_64bit).arch(arg0.os_arch).build();
 
          return new ImageBuilder().id(arg0.id).name(arg0.name).description(arg0.description)
                   .operatingSystem(operatingSystem).build();
@@ -149,4 +149,15 @@
    public Image toImage() {
       return toImage.apply(this);
    }
+
+   @Override
+   public String toString() {
+      return "YamlImage [id=" + id + ", name=" + name + ", description=" + description + ", hostname=" + hostname
+               + ", location_id=" + location_id + ", os_arch=" + os_arch + ", os_family=" + os_family
+               + ", os_description=" + os_description + ", os_version=" + os_version + ", iso=" + iso
+               + ", keystroke_sequence=" + keystroke_sequence + ", preseed_cfg=" + preseed_cfg + ", login_port="
+               + login_port + ", os_64bit=" + os_64bit + ", group=" + group + ", tags=" + tags + ", metadata="
+               + metadata + ", username=" + username + ", credential=" + credential + ", credential_url="
+               + credential_url + ", sudo_password=" + sudo_password + "]";
+   }
 }
\ No newline at end of file
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java
index ad3bc89..a848671 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java
@@ -21,8 +21,8 @@
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.Iterables.transform;
 import static org.jclouds.scriptbuilder.domain.Statements.call;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL;
 
-import java.net.URI;
 import java.util.List;
 
 import javax.annotation.Resource;
@@ -36,7 +36,6 @@
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.logging.Logger;
 import org.jclouds.ssh.SshClient;
-import org.jclouds.virtualbox.Preconfiguration;
 import org.jclouds.virtualbox.domain.IsoSpec;
 import org.jclouds.virtualbox.domain.MasterSpec;
 import org.jclouds.virtualbox.domain.VmSpec;
@@ -49,7 +48,6 @@
 import com.google.common.base.Predicate;
 import com.google.common.base.Splitter;
 import com.google.common.base.Throwables;
-import com.google.common.cache.LoadingCache;
 import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -64,28 +62,28 @@
 
    private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists;
    private final Predicate<SshClient> sshResponds;
-   private LoadingCache<IsoSpec, URI> preConfiguration;
    private final Function<IMachine, SshClient> sshClientForIMachine;
    private final MachineUtils machineUtils;
    private final IMachineToNodeMetadata imachineToNodeMetadata;
    private final MachineController machineController;
    private final String version;
+   private final String preconfigurationUrl;
 
    @Inject
    public CreateAndInstallVm(
             CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists,
             IMachineToNodeMetadata imachineToNodeMetadata, Predicate<SshClient> sshResponds,
             Function<IMachine, SshClient> sshClientForIMachine, MachineUtils machineUtils,
-            @Preconfiguration LoadingCache<IsoSpec, URI> preConfiguration, MachineController machineController,
-            @Named(Constants.PROPERTY_BUILD_VERSION) String version) {
+            MachineController machineController, @Named(Constants.PROPERTY_BUILD_VERSION) String version,
+            @Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) {
       this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
       this.sshResponds = sshResponds;
       this.sshClientForIMachine = sshClientForIMachine;
       this.machineUtils = machineUtils;
-      this.preConfiguration = preConfiguration;
       this.imachineToNodeMetadata = imachineToNodeMetadata;
       this.machineController = machineController;
       this.version = Iterables.get(Splitter.on('r').split(version), 0);
+      this.preconfigurationUrl = preconfigurationUrl;
    }
 
    @Override
@@ -100,9 +98,8 @@
       // Launch machine and wait for it to come online
       machineController.ensureMachineIsLaunched(vmName);
 
-      URI uri = preConfiguration.getUnchecked(isoSpec);
       String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL",
-               uri.toASCIIString());
+               preconfigurationUrl);
 
       configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence);
 
@@ -112,7 +109,7 @@
       } catch (InterruptedException e) {
          Throwables.propagate(e);
       }
-      
+
       SshClient client = sshClientForIMachine.apply(vm);
 
       logger.debug(">> awaiting installation to finish node(%s)", vmName);
@@ -127,7 +124,6 @@
       ExecResponse cleanupResponse = Futures.getUnchecked(execCleanup);
       checkState(cleanupResponse.getExitStatus() == 0);
 
-      
       logger.debug(">> awaiting installation of guest additions on vm: %s", vmName);
       ListenableFuture<ExecResponse> execInstallGA = machineUtils.runScriptOnNode(nodeMetadata,
                new InstallGuestAdditions(vmSpec, version), RunScriptOptions.NONE);
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java
index 2c75646..5aa6682 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java
@@ -19,12 +19,14 @@
 
 package org.jclouds.virtualbox.functions;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_DIR;
 import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
 import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
 import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL;
 import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR;
 import static org.jclouds.virtualbox.util.MachineUtils.machineNotFoundException;
 
@@ -54,6 +56,7 @@
 import org.jclouds.virtualbox.domain.StorageController;
 import org.jclouds.virtualbox.domain.VmSpec;
 import org.jclouds.virtualbox.domain.YamlImage;
+import org.jclouds.virtualbox.functions.admin.PreseedCfgServer;
 import org.virtualbox_4_1.CleanupMode;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.NetworkAttachmentType;
@@ -72,7 +75,8 @@
 /**
  * A {@link LoadingCache} for masters. If the requested master has been previously created this
  * returns it, if not it coordinates its creation including downloading isos and creating
- * cache/config directories.
+ * cache/config directories. This also implements {@link Supplier} in order to provide jetty with
+ * the current image (only one master can be created at a time).
  * 
  * @author dralves
  * 
@@ -94,13 +98,15 @@
    private final String workingDir;
    private final String installationKeySequence;
    private final String isosDir;
-   private Supplier<VirtualBoxManager> manager;
-   private Function<URI, File> isoDownloader;
-   private String version;
+   private final Supplier<VirtualBoxManager> manager;
+   private final Function<URI, File> isoDownloader;
+   private final String version;
+   private final String preconfigurationUrl;
 
    @Inject
    public MastersLoadingCache(@Named(Constants.PROPERTY_BUILD_VERSION) String version,
             @Named(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE) String installationKeySequence,
+            @Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl,
             @Named(VIRTUALBOX_WORKINGDIR) String workingDir, Function<MasterSpec, IMachine> masterLoader,
             Supplier<Map<Image, YamlImage>> yamlMapper, Supplier<VirtualBoxManager> manager,
             Function<URI, File> isoDownloader) {
@@ -118,6 +124,7 @@
       }
       this.version = Iterables.get(Splitter.on('r').split(version), 0);
       this.isoDownloader = isoDownloader;
+      this.preconfigurationUrl = preconfigurationUrl;
    }
 
    @PostConstruct
@@ -135,11 +142,11 @@
       }
 
       // the yaml image
-      YamlImage yamlImage = imageMapping.get(key.getId());
+      YamlImage currentImage = imageMapping.get(key.getId());
 
-      checkNotNull(yamlImage, "could not find yaml image for image: " + key);
+      checkNotNull(currentImage, "could not find yaml image for image: " + key);
       
-      checkState(!yamlImage.id.contains(VIRTUALBOX_NODE_NAME_SEPARATOR), "master image names cannot contain \""
+      checkState(!currentImage.id.contains(VIRTUALBOX_NODE_NAME_SEPARATOR), "master image names cannot contain \""
                + VIRTUALBOX_NODE_NAME_SEPARATOR + "\"");
 
       String guestAdditionsFileName = String.format("VBoxGuestAdditions_%s.iso", version);
@@ -151,9 +158,9 @@
       checkState(new File(guestAdditionsIso).exists(), "guest additions iso does not exist at: " + guestAdditionsIso);
 
       // check if the iso is here, download if not
-      String localIsoUrl = getFilePathOrDownload(yamlImage.iso);
+      String localIsoUrl = getFilePathOrDownload(currentImage.iso);
 
-      String vmName = VIRTUALBOX_IMAGE_PREFIX + yamlImage.id;
+      String vmName = VIRTUALBOX_IMAGE_PREFIX + currentImage.id;
 
       String adminDisk = workingDir + File.separator + vmName + ".vdi";
 
@@ -163,7 +170,7 @@
       StorageController ideController = StorageController.builder().name("IDE Controller").bus(StorageBus.IDE)
                .attachISO(0, 0, localIsoUrl).attachHardDisk(hardDisk).attachISO(1, 0, guestAdditionsIso).build();
 
-      VmSpec vmSpecification = VmSpec.builder().id(yamlImage.id).name(vmName).memoryMB(512).osTypeId("")
+      VmSpec vmSpecification = VmSpec.builder().id(currentImage.id).name(vmName).memoryMB(512).osTypeId("")
                .controller(ideController).forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();
 
       NetworkAdapter networkAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
@@ -183,17 +190,24 @@
 
       IMachine masterMachine;
 
-      // try and find a master machine in vbox
+      
+      // ready the preseed file server
+      PreseedCfgServer server = new PreseedCfgServer();
       try {
+         // try and find a master machine in vbox
          masterMachine = manager.get().getVBox().findMachine(vmName);
       } catch (VBoxException e) {
          if (machineNotFoundException(e)) {
+            server.start(preconfigurationUrl,currentImage.preseed_cfg);
             // create the master machine if it can't be found
             masterMachine = masterCreatorAndInstaller.apply(masterSpec);
          } else {
             throw e;
          }
+      } finally {
+         server.stop();
       }
+      
 
       Master master = Master.builder().machine(masterMachine).spec(masterSpec).build();
 
@@ -203,9 +217,11 @@
    }
 
    @Override
-   public synchronized Master getIfPresent(Image key) {
-      if (masters.containsKey(key.getId())) {
-         return masters.get(key.getId());
+   public synchronized Master getIfPresent(Object key) {
+      checkArgument(key instanceof Image, "this cache is for entries who's keys are Images");
+      Image image = Image.class.cast(key);
+      if (masters.containsKey(image.getId())) {
+         return masters.get(image.getId());
       }
       return null;
    }
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/ImagesToYamlImagesFromYamlDescriptor.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/ImagesToYamlImagesFromYamlDescriptor.java
index 5d0b373..6bb5b92 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/ImagesToYamlImagesFromYamlDescriptor.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/ImagesToYamlImagesFromYamlDescriptor.java
@@ -48,7 +48,7 @@
    private String yamlDescriptor;
 
    @Inject
-   public ImagesToYamlImagesFromYamlDescriptor(/*Supplier<String> yamlDescriptorSupplier*/YamlImagesFromFileConfig yamlDescriptorSupplier) {
+   public ImagesToYamlImagesFromYamlDescriptor(YamlImagesFromFileConfig yamlDescriptorSupplier) {
       this.yamlDescriptor = yamlDescriptorSupplier.get();
       checkNotNull(yamlDescriptor, "yaml descriptor");
       checkState(!yamlDescriptor.equals(""), "yaml descriptor is empty");
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/PreseedCfgServer.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/PreseedCfgServer.java
new file mode 100644
index 0000000..d8ce88e
--- /dev/null
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/PreseedCfgServer.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.virtualbox.functions.admin;
+
+import java.io.IOException;
+import java.net.URI;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+
+import com.google.common.base.Throwables;
+
+/**
+ * Sets up jetty so that it can serve the preseed.cfg file to automate master creation.
+ * 
+ * @author Andrea Turli, David Alves
+ */
+public class PreseedCfgServer {
+
+   private Server jetty;
+
+   public void start(String preconfigurationUrl, final String preseedCfg) {
+      this.jetty = new Server(URI.create(preconfigurationUrl).getPort());
+      try {
+         // since we're only serving the preseed.cfg file respond to all requests with it
+         jetty.setHandler(new AbstractHandler() {
+            @Override
+            public void handle(String target, Request baseRequest, HttpServletRequest request,
+                     HttpServletResponse response) throws IOException, ServletException {
+               response.setContentType("text/plain;charset=utf-8");
+               response.setStatus(HttpServletResponse.SC_OK);
+               baseRequest.setHandled(true);
+               response.getWriter().println(preseedCfg);
+            }
+         });
+         jetty.start();
+      } catch (Exception e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   public void stop() {
+      try {
+         if (jetty != null) {
+            jetty.stop();
+         }
+      } catch (Exception e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+}
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunning.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunning.java
deleted file mode 100644
index f43f398..0000000
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunning.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.jclouds.virtualbox.functions.admin;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Throwables.propagate;
-import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL;
-
-import java.io.IOException;
-import java.net.URI;
-
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.io.IOUtils;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.util.resource.Resource;
-import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.logging.Logger;
-import org.jclouds.virtualbox.domain.IsoSpec;
-
-import com.google.common.base.Throwables;
-import com.google.common.cache.CacheLoader;
-import com.google.inject.Singleton;
-
-/**
- * Sets up jetty so that it can serve the preseed.cfg file to automate master creation.
- * 
- * TODO - Probably we can make this only start jetty. This has not been used to serve isos.
- * 
- * @author Andrea Turli, David Alves
- */
-@Singleton
-public class StartJettyIfNotAlreadyRunning extends CacheLoader<IsoSpec, URI> {
-
-   @javax.annotation.Resource
-   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
-   protected Logger logger = Logger.NULL;
-   private Server jetty;
-   private final String preconfigurationUrl;
-
-   @Inject
-   public StartJettyIfNotAlreadyRunning(@Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl, Server jetty) {
-      this.preconfigurationUrl = preconfigurationUrl;
-      this.jetty = jetty;
-   }
-
-   @Override
-   public URI load(IsoSpec isoSpec) throws Exception {
-      try {
-         start();
-      } catch (Exception e) {
-         logger.error("Could not connect to host providing ISO " + isoSpec, e);
-         propagate(e);
-      }
-      return URI.create(preconfigurationUrl);
-   }
-
-   private void start() {
-      if (jetty.getState().equals(Server.STARTED)) {
-         logger.debug("not starting jetty, as existing host is serving %s", preconfigurationUrl);
-      } else {
-         try {
-
-            // find the the parent dir inside the jar to serve the file from
-
-            final String preseedFile = IOUtils
-                     .toString(Resource.newSystemResource("preseed.cfg").getURL().openStream());
-
-            checkState(preseedFile != null);
-
-            // since we're only serving the preseed.cfg file respond to all requests with it
-            jetty.setHandler(new AbstractHandler() {
-
-               @Override
-               public void handle(String target, Request baseRequest, HttpServletRequest request,
-                        HttpServletResponse response) throws IOException, ServletException {
-                  response.setContentType("text/plain;charset=utf-8");
-                  response.setStatus(HttpServletResponse.SC_OK);
-                  baseRequest.setHandled(true);
-                  response.getWriter().println(preseedFile);
-               }
-
-            });
-
-            jetty.start();
-
-         } catch (Exception e) {
-            logger.error(e, "Server jetty could not be started for %s", preconfigurationUrl);
-            throw Throwables.propagate(e);
-         }
-      }
-   }
-
-   @PreDestroy()
-   public void stop() {
-      try {
-         jetty.stop();
-      } catch (Exception e) {
-         logger.error("Could not stop jetty.", e);
-      }
-   }
-
-}
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java
index 9cf475f..dda2cb3 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java
@@ -59,8 +59,6 @@
    private final RetryIfSocketNotYetOpen socketTester;
    private final Supplier<NodeMetadata> host;
    private final Supplier<URI> providerSupplier;
-//   private final String identity;
-//   private final String credential;
    private final Function<Supplier<NodeMetadata>, VirtualBoxManager> managerForNode;
    private transient VirtualBoxManager manager;
 
@@ -75,8 +73,6 @@
       this.socketTester.seconds(3L);
       this.host = checkNotNull(host, "host");
       this.providerSupplier = checkNotNull(providerSupplier, "endpoint to virtualbox websrvd is needed");
-//      this.identity = checkNotNull(identity, "identity");
-//      this.credential = checkNotNull(credential, "credential");
       this.managerForNode = checkNotNull(managerForNode, "managerForNode");
    }
 
@@ -89,10 +85,6 @@
                   runAsRoot(false).wrapInInitScript(false)).init().call();
          logger.debug(">> starting vboxwebsrv");
          String vboxwebsrv = "vboxwebsrv -t 10000 -v -b";
-         if (host.get().getOperatingSystem() != null
-                  && host.get().getOperatingSystem().getDescription().equals("Mac OS X"))
-            vboxwebsrv = "cd /Applications/VirtualBox.app/Contents/MacOS/ && " + vboxwebsrv;
-
          runScriptOnNodeFactory.create(host.get(), Statements.exec(vboxwebsrv),
                   runAsRoot(false).wrapInInitScript(false).blockOnComplete(false).nameTask("vboxwebsrv")).init().call();
          
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/DefaultImagePredicate.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/DefaultImagePredicate.java
new file mode 100644
index 0000000..0221274
--- /dev/null
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/DefaultImagePredicate.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.virtualbox.predicates;
+
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_ARCH;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_OS;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_VERSION;
+
+import org.jclouds.compute.domain.Image;
+
+import com.google.common.base.Predicate;
+
+public class DefaultImagePredicate implements Predicate<Image> {
+
+   @Override
+   public boolean apply(Image input) {
+      return input.getOperatingSystem().getFamily() == VIRTUALBOX_DEFAULT_IMAGE_OS
+               && input.getOperatingSystem().getVersion().equals(VIRTUALBOX_DEFAULT_IMAGE_VERSION)
+               && input.getOperatingSystem().getArch().equals(VIRTUALBOX_DEFAULT_IMAGE_ARCH);
+   }
+}
diff --git a/labs/virtualbox/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/labs/virtualbox/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..6eeabb6
--- /dev/null
+++ b/labs/virtualbox/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.virtualbox.VirtualBoxApiMetadata
\ No newline at end of file
diff --git a/labs/virtualbox/src/main/resources/default-images.yaml b/labs/virtualbox/src/main/resources/default-images.yaml
index 7927b93..84380c2 100644
--- a/labs/virtualbox/src/main/resources/default-images.yaml
+++ b/labs/virtualbox/src/main/resources/default-images.yaml
@@ -1,5 +1,5 @@
 images:
-    - id: default-ubuntu-11.04-i386
+    - id: ubuntu-11.04-i386
       name: ubuntu-11.04-server-i386
       description: ubuntu 11.04 server (i386)
       os_arch: x86
@@ -66,3 +66,70 @@
                       # debconf-get-selections --install
                       #Use mirror
                       choose-mirror-bin mirror/http/proxy string
+    - id: ubuntu-11.10-i386
+      name: ubuntu-11.10-server-i386
+      description: ubuntu 11.10 server (i386)
+      os_arch: x86
+      os_family: ubuntu
+      os_description: ubuntu
+      os_version: 11.10
+      iso: http://releases.ubuntu.com/11.10/ubuntu-11.10-server-i386.iso
+      keystroke_sequence: |
+                <Esc><Esc><Enter> 
+                /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg 
+                debian-installer=en_US auto locale=en_US kbd-chooser/method=us 
+                hostname=vmName 
+                fb=false debconf/frontend=noninteractive 
+                keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false 
+                initrd=/install/initrd.gz -- <Enter>
+      preseed_cfg: |
+                      ## Options to set on the command line
+                      d-i debian-installer/locale string en_US.utf8
+                      d-i console-setup/ask_detect boolean false
+                      d-i console-setup/layout string USA
+                      d-i netcfg/get_hostname string unassigned-hostname
+                      d-i netcfg/get_domain string unassigned-domain
+                      # Continue without a default route
+                      # Not working , specify a dummy in the DHCP
+                      d-i time/zone string UTC
+                      d-i clock-setup/utc-auto boolean true
+                      d-i clock-setup/utc boolean true
+                      d-i kbd-chooser/method    select  American English
+                      d-i netcfg/wireless_wep string
+                      d-i base-installer/kernel/override-image string linux-server
+                      # Choices: Dialog, Readline, Gnome, Kde, Editor, Noninteractive
+                      d-i debconf debconf/frontend select Noninteractive
+                      d-i pkgsel/install-language-support boolean false
+                      tasksel tasksel/first multiselect standard, ubuntu-server
+                      d-i partman-auto/method string lvm
+                      d-i partman-lvm/confirm boolean true
+                      d-i partman-lvm/device_remove_lvm boolean true
+                      d-i partman-auto/choose_recipe select atomic
+                      d-i partman/confirm_write_new_label boolean true
+                      d-i partman/confirm_nooverwrite boolean true
+                      d-i partman/choose_partition select finish
+                      d-i partman/confirm boolean true
+                      # Write the changes to disks and configure LVM?
+                      d-i partman-lvm/confirm boolean true
+                      d-i partman-lvm/confirm_nooverwrite boolean true
+                      d-i partman-auto-lvm/guided_size string max
+                      ## Default user, we can get away with a recipe to change this
+                      d-i passwd/user-fullname string toor
+                      d-i passwd/username string toor
+                      d-i passwd/user-password password password
+                      d-i passwd/user-password-again password password
+                      d-i user-setup/encrypt-home boolean false
+                      d-i user-setup/allow-password-weak boolean true
+                      # Individual additional packages to install
+                      d-i pkgsel/include string openssh-server ntp
+                      # Whether to upgrade packages after debootstrap.
+                      # Allowed values: none, safe-upgrade, full-upgrade
+                      d-i pkgsel/upgrade select full-upgrade
+                      d-i grub-installer/only_debian boolean true
+                      d-i grub-installer/with_other_os boolean true
+                      d-i finish-install/reboot_in_progress note
+                      #For the update
+                      d-i pkgsel/update-policy select none
+                      # debconf-get-selections --install
+                      #Use mirror
+                      choose-mirror-bin mirror/http/proxy string
\ No newline at end of file
diff --git a/labs/virtualbox/src/main/resources/preseed.cfg b/labs/virtualbox/src/main/resources/preseed.cfg
deleted file mode 100644
index 5a972d1..0000000
--- a/labs/virtualbox/src/main/resources/preseed.cfg
+++ /dev/null
@@ -1,88 +0,0 @@
-## Options to set on the command line
-d-i debian-installer/locale string en_US.utf8
-d-i console-setup/ask_detect boolean false
-d-i console-setup/layout string USA
-
-#d-i netcfg/get_hostname string dummy
-d-i netcfg/get_hostname string unassigned-hostname
-d-i netcfg/get_domain string unassigned-domain
-
-# Continue without a default route
-# Not working , specify a dummy in the DHCP
-#d-i netcfg/no_default_route boolean
-
-d-i time/zone string UTC
-d-i clock-setup/utc-auto boolean true
-d-i clock-setup/utc boolean true
-
-d-i kbd-chooser/method	select	American English
-
-d-i netcfg/wireless_wep string
-
-d-i base-installer/kernel/override-image string linux-server
-#d-i base-installer/kernel/override-image string linux-image-2.6.32-21-generic
-
-# Choices: Dialog, Readline, Gnome, Kde, Editor, Noninteractive
-d-i debconf debconf/frontend select Noninteractive
-
-d-i pkgsel/install-language-support boolean false
-tasksel tasksel/first multiselect standard, ubuntu-server
-
-#d-i partman-auto/method string regular
-d-i partman-auto/method string lvm
-#d-i partman-auto/purge_lvm_from_device boolean true
-
-d-i partman-lvm/confirm boolean true
-d-i partman-lvm/device_remove_lvm boolean true
-d-i partman-auto/choose_recipe select atomic
-
-d-i partman/confirm_write_new_label boolean true
-d-i partman/confirm_nooverwrite boolean true
-d-i partman/choose_partition select finish
-d-i partman/confirm boolean true
-
-#http://ubuntu-virginia.ubuntuforums.org/showthread.php?p=9626883
-#Message: "write the changes to disk and configure lvm preseed"
-#http://serverfault.com/questions/189328/ubuntu-kickstart-installation-using-lvm-waits-for-input
-#preseed partman-lvm/confirm_nooverwrite boolean true
-
-# Write the changes to disks and configure LVM?
-d-i partman-lvm/confirm boolean true
-d-i partman-lvm/confirm_nooverwrite boolean true
-d-i partman-auto-lvm/guided_size string max
-
-## Default user, we can get away with a recipe to change this
-d-i passwd/user-fullname string toor
-d-i passwd/username string toor
-d-i passwd/user-password password password
-d-i passwd/user-password-again password password
-d-i user-setup/encrypt-home boolean false
-d-i user-setup/allow-password-weak boolean true
-
-## minimum is puppet and ssh and ntp
-# Individual additional packages to install
-d-i pkgsel/include string openssh-server ntp
-# dkms build-essential module-assistant
-
-# Whether to upgrade packages after debootstrap.
-# Allowed values: none, safe-upgrade, full-upgrade
-d-i pkgsel/upgrade select full-upgrade
-
-d-i grub-installer/only_debian boolean true
-d-i grub-installer/with_other_os boolean true
-d-i finish-install/reboot_in_progress note
-
-#For the update
-d-i pkgsel/update-policy select none
-
-# debconf-get-selections --install
-#Use mirror
-#d-i apt-setup/use_mirror boolean true
-#d-i     mirror/country          string manual
-#choose-mirror-bin mirror/protocol	string http
-#choose-mirror-bin mirror/http/hostname	string 192.168.4.150
-#choose-mirror-bin mirror/http/directory	string /ubuntu
-#choose-mirror-bin mirror/suite	select maverick
-#d-i debian-installer/allow_unauthenticated	string true
-
-choose-mirror-bin mirror/http/proxy string
\ No newline at end of file
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 75ab267..fc6ecf1 100644
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java
@@ -20,10 +20,11 @@
 package org.jclouds.virtualbox;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
 import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
 
 import java.io.File;
-import java.net.URI;
+import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.ExecutorService;
 
@@ -52,11 +53,16 @@
 import org.jclouds.virtualbox.domain.NetworkSpec;
 import org.jclouds.virtualbox.domain.StorageController;
 import org.jclouds.virtualbox.domain.VmSpec;
+import org.jclouds.virtualbox.domain.YamlImage;
 import org.jclouds.virtualbox.functions.IMachineToVmSpec;
+import org.jclouds.virtualbox.functions.YamlImagesFromFileConfig;
+import org.jclouds.virtualbox.functions.admin.ImagesToYamlImagesFromYamlDescriptor;
 import org.jclouds.virtualbox.functions.admin.UnregisterMachineIfExistsAndDeleteItsMedia;
+import org.jclouds.virtualbox.predicates.DefaultImagePredicate;
 import org.jclouds.virtualbox.util.MachineController;
 import org.jclouds.virtualbox.util.MachineUtils;
 import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterSuite;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.virtualbox_4_1.CleanupMode;
@@ -83,12 +89,15 @@
  */
 @Test(groups = "live", singleThreaded = true, testName = "BaseVirtualBoxClientLiveTest")
 public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest {
+
+   public static final String DONT_DESTROY_MASTER = "jclouds.virtualbox.keep-test-master";
+
    public BaseVirtualBoxClientLiveTest() {
       provider = "virtualbox";
    }
-   
+
    protected ComputeServiceContext context;
-   
+
    @Inject
    protected MachineController machineController;
 
@@ -104,11 +113,6 @@
    @Inject
    protected MachineUtils machineUtils;
 
-   // this will eagerly startup Jetty, note the impl will shut itself down
-   @Inject
-   @Preconfiguration
-   protected LoadingCache<IsoSpec, URI> preconfigurationUri;
-
    protected String hostVersion;
    protected String operatingSystemIso;
    protected String guestAdditionsIso;
@@ -122,8 +126,9 @@
    protected PrioritizeCredentialsFromTemplate prioritizeCredentialsFromTemplate;
    @Inject
    protected LoadingCache<Image, Master> mastersCache;
-   
-   private final ExecutorService singleThreadExec = MoreExecutors.sameThreadExecutor(); 
+
+   private final ExecutorService singleThreadExec = MoreExecutors.sameThreadExecutor();
+   private String masterVmName;
 
    @Override
    protected void setupCredentials() {
@@ -147,16 +152,19 @@
       context = new ComputeServiceContextFactory().createContext(provider, identity, credential, ImmutableSet
                .<Module> of(new SLF4JLoggingModule(), new SshjSshClientModule(), new ExecutorServiceModule(
                         singleThreadExec, singleThreadExec)), overrides);
-      
+
       context.utils().injector().injectMembers(this);
 
-      imageId = "ubuntu-11.04-server-i386";
+      YamlImage image = getDefaultImage();
+
+      imageId = image.id;
+      masterVmName = VIRTUALBOX_IMAGE_PREFIX + image.id;
       isosDir = workingDir + File.separator + "isos";
 
       hostVersion = Iterables.get(Splitter.on('r').split(context.getProviderSpecificContext().getBuildVersion()), 0);
-      operatingSystemIso = String.format("%s/%s.iso", isosDir, imageId);
+      operatingSystemIso = String.format("%s/%s.iso", isosDir, image.name);
       guestAdditionsIso = String.format("%s/VBoxGuestAdditions_%s.iso", isosDir, hostVersion);
-      
+
       // try and get a master from the cache, this will initialize the config/download isos and
       // prepare everything IF a master is not available, subsequent calls should be pretty fast
       Template template = context.getComputeService().templateBuilder().build();
@@ -189,7 +197,8 @@
    }
 
    public MasterSpec getMasterSpecForTest() {
-      String masterName = "jclouds-image-0x0-default-ubuntu-11.04-i386";
+      String masterName = "jclouds-image-0x0-" + imageId;
+
       StorageController ideController = StorageController
                .builder()
                .name("IDE Controller")
@@ -199,37 +208,45 @@
                         HardDisk.builder().diskpath(adminDisk(masterName)).controllerPort(0).deviceSlot(1)
                                  .autoDelete(true).build()).attachISO(1, 0, guestAdditionsIso).build();
 
-            VmSpec sourceVmSpec = VmSpec.builder().id(masterName).name(masterName)
-                        .osTypeId("").memoryMB(512).cleanUpMode(CleanupMode.Full)
-                        .controller(ideController).forceOverwrite(true).build();
+      VmSpec sourceVmSpec = VmSpec.builder().id(masterName).name(masterName).osTypeId("").memoryMB(512)
+               .cleanUpMode(CleanupMode.Full).controller(ideController).forceOverwrite(true).build();
 
-            Injector injector = context.utils().injector();
-            Function<String, String> configProperties = injector
-                        .getInstance(ValueOfConfigurationKeyOrNull.class);
-            IsoSpec isoSpec = IsoSpec
-                        .builder()
-                        .sourcePath(operatingSystemIso)
-                        .installationScript(
-                                    configProperties.apply(
-                                                VIRTUALBOX_INSTALLATION_KEY_SEQUENCE).replace(
-                                                "HOSTNAME", sourceVmSpec.getVmName())).build();
-            
-            NetworkAdapter networkAdapter = NetworkAdapter.builder()
-                        .networkAttachmentType(NetworkAttachmentType.NAT)
-                        .tcpRedirectRule("127.0.0.1", 2222, "", 22).build();
-            NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard
-                        .builder().addNetworkAdapter(networkAdapter).build();
+      Injector injector = context.utils().injector();
+      Function<String, String> configProperties = injector.getInstance(ValueOfConfigurationKeyOrNull.class);
+      IsoSpec isoSpec = IsoSpec
+               .builder()
+               .sourcePath(operatingSystemIso)
+               .installationScript(
+                        configProperties.apply(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE).replace("HOSTNAME",
+                                 sourceVmSpec.getVmName())).build();
 
-            NetworkSpec networkSpec = NetworkSpec.builder()
-                        .addNIC(networkInterfaceCard).build();
-            return MasterSpec.builder().iso(isoSpec).vm(sourceVmSpec)
-                        .network(networkSpec).build();
+      NetworkAdapter networkAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
+               .tcpRedirectRule("127.0.0.1", 2222, "", 22).build();
+      NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
+               .build();
+
+      NetworkSpec networkSpec = NetworkSpec.builder().addNIC(networkInterfaceCard).build();
+      return MasterSpec.builder().iso(isoSpec).vm(sourceVmSpec).network(networkSpec).build();
    }
-   
+
+   public static YamlImage getDefaultImage() {
+      Map<Image, YamlImage> images = new ImagesToYamlImagesFromYamlDescriptor(new YamlImagesFromFileConfig(
+               "/default-images.yaml")).get();
+      return images.get(Iterables.getOnlyElement(Iterables.filter(images.keySet(), new DefaultImagePredicate())));
+   }
+
    @AfterClass(groups = "live")
    protected void tearDown() throws Exception {
       if (context != null)
          context.close();
    }
 
+   @AfterSuite
+   protected void destroyMaster() {
+      if (System.getProperty(DONT_DESTROY_MASTER) == null
+               || !Boolean.parseBoolean(System.getProperty(DONT_DESTROY_MASTER))) {
+         undoVm(masterVmName);
+      }
+   }
+
 }
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/StartJettyIfNotAlreadyRunningLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/PreseedCfgServerTest.java
similarity index 72%
rename from labs/virtualbox/src/test/java/org/jclouds/virtualbox/StartJettyIfNotAlreadyRunningLiveTest.java
rename to labs/virtualbox/src/test/java/org/jclouds/virtualbox/PreseedCfgServerTest.java
index 5a18cbe..2dc1358 100644
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/StartJettyIfNotAlreadyRunningLiveTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/PreseedCfgServerTest.java
@@ -25,20 +25,19 @@
 import java.util.Properties;
 
 import org.apache.commons.io.IOUtils;
-import org.eclipse.jetty.server.Server;
 import org.jclouds.virtualbox.config.VirtualBoxConstants;
-import org.jclouds.virtualbox.functions.admin.StartJettyIfNotAlreadyRunning;
+import org.jclouds.virtualbox.functions.admin.PreseedCfgServer;
 import org.testng.annotations.Test;
 
 /**
- * Tests that jetty is able to serve the preseed.cfg file. This test is here to have access to the
- * defaultProperties() method in {@link VirtualBoxPropertiesBuilder}.
+ * Tests that jetty is able to serve the preseed.cfg from the provided yaml image. This test is here
+ * to have access to the defaultProperties() method in {@link VirtualBoxPropertiesBuilder}.
  * 
  * @author dralves
  * 
  */
 @Test(groups = "live", singleThreaded = true, testName = "StartJettyIfNotAlreadyRunningLiveTest")
-public class StartJettyIfNotAlreadyRunningLiveTest {
+public class PreseedCfgServerTest {
 
    @Test
    public void testJettyServerServesPreseedFile() throws Exception {
@@ -48,15 +47,14 @@
 
       int port = URI.create(preconfigurationUrl).getPort();
 
-      Server server = new Server(port);
+      PreseedCfgServer starter = new PreseedCfgServer();
 
-      StartJettyIfNotAlreadyRunning starter = new StartJettyIfNotAlreadyRunning(preconfigurationUrl, server);
-
-      starter.load(null);
+      starter.start(preconfigurationUrl, BaseVirtualBoxClientLiveTest.getDefaultImage().preseed_cfg);
 
       String preseedFileFromJetty = IOUtils.toString(new URL("http://127.0.0.1:" + port + "/preseed.cfg").openStream());
-      String preseedFileFromFile = IOUtils
-               .toString(this.getClass().getClassLoader().getResourceAsStream("preseed.cfg")) + "\n";
+      String preseedFileFromFile = BaseVirtualBoxClientLiveTest.getDefaultImage().preseed_cfg + "\n";
       assertEquals(preseedFileFromFile, preseedFileFromJetty);
+
+      starter.stop();
    }
 }
diff --git a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/VirtualBoxApiMetadataTest.java
similarity index 69%
copy from sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
copy to labs/virtualbox/src/test/java/org/jclouds/virtualbox/VirtualBoxApiMetadataTest.java
index ace0f456..4880ed4 100644
--- a/sandbox-providers/aws-simpledb/src/test/java/org/jclouds/aws/simpledb/ProvidersInPropertiesTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/VirtualBoxApiMetadataTest.java
@@ -16,25 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.aws.simpledb;
+package org.jclouds.virtualbox;
 
-import org.jclouds.rest.Providers;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadataTest;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Iterables;
-
 /**
  * 
  * @author Adrian Cole
- * 
  */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-   
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "aws-simpledb") : providers;
-   }
+@Test(groups = "unit", testName = "VirtualBoxApiMetadataTest")
+public class VirtualBoxApiMetadataTest extends BaseApiMetadataTest {
 
+   public VirtualBoxApiMetadataTest() {
+      super(new VirtualBoxApiMetadata(), ApiType.COMPUTE);
+   }
 }
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterLiveTest.java
index a3d61ba..b01067c 100644
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterLiveTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapterLiveTest.java
@@ -19,6 +19,7 @@
 
 package org.jclouds.virtualbox.compute;
 
+import static junit.framework.Assert.assertTrue;
 import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
 import static org.testng.Assert.assertEquals;
 
@@ -42,7 +43,7 @@
 public class VirtualBoxComputeServiceAdapterLiveTest extends BaseVirtualBoxClientLiveTest {
 
    private NodeAndInitialCredentials<IMachine> machine;
-   
+
    @Inject
    protected VirtualBoxComputeServiceAdapter adapter;
 
@@ -50,14 +51,14 @@
    public void testCreatedNodeHasExpectedNameAndWeCanConnectViaSsh() {
       String group = "foo";
       String name = "foo-ef4";
-      String machineName = VIRTUALBOX_NODE_PREFIX + "default-ubuntu-11.04-i386-" + "0x0-" + group + "-0x0-" + name;
       Template template = context.getComputeService().templateBuilder().build();
       machine = adapter.createNodeWithGroupEncodedIntoName(group, name, template);
-      
-      assertEquals(machine.getNode().getName(), machineName);
+      assertTrue(machine.getNode().getName().contains(group));
+      assertTrue(machine.getNode().getName().contains(name));
+      assertTrue(machine.getNode().getName().startsWith(VIRTUALBOX_NODE_PREFIX));
       doConnectViaSsh(machine.getNode(), prioritizeCredentialsFromTemplate.apply(template, machine.getCredentials()));
    }
-   
+
    protected void doConnectViaSsh(IMachine machine, LoginCredentials creds) {
       SshClient ssh = context.utils().injector().getInstance(IMachineToSshClient.class).apply(machine);
       try {
@@ -76,15 +77,13 @@
    @Test
    public void testListHardwareProfiles() {
       Iterable<IMachine> profiles = adapter.listHardwareProfiles();
-      assertEquals(1, Iterables.size(profiles));
-      //TODO: check state;
+      assertTrue(!Iterables.isEmpty(profiles));
    }
 
    @Test
    public void testListImages() {
       Iterable<Image> iMageIterable = adapter.listImages();
-      assertEquals(1, Iterables.size(iMageIterable));
-      //TODO: check state;
+      assertTrue(!Iterables.isEmpty(iMageIterable));
    }
 
    @AfterClass
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java
index 6415236..a13cf56 100644
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java
@@ -39,6 +39,7 @@
 import org.jclouds.scriptbuilder.statements.login.AdminAccess;
 import org.jclouds.ssh.SshClient;
 import org.jclouds.sshj.config.SshjSshClientModule;
+import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -51,7 +52,7 @@
  * @author Adrian Cole
  */
 @Test(groups = "live", singleThreaded = true, testName = "VirtualBoxExperimentLiveTest")
-public class VirtualBoxExperimentLiveTest {
+public class VirtualBoxExperimentLiveTest extends BaseVirtualBoxClientLiveTest {
 
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
@@ -69,7 +70,8 @@
    public void testLaunchCluster() throws RunNodesException {
       int numNodes = 2;
       final String clusterName = "test-launch-cluster";
-      Set<? extends NodeMetadata> nodes = context.getComputeService().createNodesInGroup(clusterName, numNodes, TemplateOptions.Builder.runScript(AdminAccess.standard()));
+      Set<? extends NodeMetadata> nodes = context.getComputeService().createNodesInGroup(clusterName, numNodes,
+               TemplateOptions.Builder.runScript(AdminAccess.standard()));
       assertEquals(numNodes, nodes.size(), "wrong number of nodes");
       for (NodeMetadata node : nodes) {
          assertTrue(node.getGroup().equals("test-launch-cluster"));
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/ImageFromYamlStringTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/ImageFromYamlStringTest.java
index 8998ceb..42d62ab 100644
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/ImageFromYamlStringTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/ImageFromYamlStringTest.java
@@ -19,16 +19,19 @@
 
 package org.jclouds.virtualbox.functions.admin;
 
+import static junit.framework.Assert.assertTrue;
 import static org.testng.Assert.assertEquals;
 
-import java.io.InputStreamReader;
+import java.util.Map;
 
-import org.apache.commons.io.IOUtils;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.ImageBuilder;
 import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.virtualbox.domain.YamlImage;
 import org.jclouds.virtualbox.functions.YamlImagesFromFileConfig;
+import org.jclouds.virtualbox.predicates.DefaultImagePredicate;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.Iterables;
@@ -40,22 +43,31 @@
 public class ImageFromYamlStringTest {
 
    public static final Image TEST1 = new ImageBuilder()
-            .id("default-ubuntu-11.04-i386")
+            .id("ubuntu-11.04-i386")
             .name("ubuntu-11.04-server-i386")
             .description("ubuntu 11.04 server (i386)")
             .operatingSystem(
-                     OperatingSystem.builder().description("ubuntu").family(OsFamily.UBUNTU).version("11.04").build())
-            .build();
+                     OperatingSystem.builder().description("ubuntu").family(OsFamily.UBUNTU).version("11.04")
+                              .arch("x86").build()).build();
+
+   Map<Image, YamlImage> images;
+
+   @BeforeMethod
+   public void setUp() {
+      images = new ImagesToYamlImagesFromYamlDescriptor(new YamlImagesFromFileConfig("/default-images.yaml")).get();
+   }
 
    @Test
-   public void testNodesParse() throws Exception {
+   public void testNodesParse() {
+      assertEquals(Iterables.getFirst(images.keySet(), null), TEST1);
+   }
 
-      final StringBuilder yamlFileLines = new StringBuilder();
-      for (Object line : IOUtils
-               .readLines(new InputStreamReader(getClass().getResourceAsStream("/default-images.yaml")))) {
-         yamlFileLines.append(line).append("\n");
-      }
-       ImagesToYamlImagesFromYamlDescriptor parser = new ImagesToYamlImagesFromYamlDescriptor(new YamlImagesFromFileConfig("/default-images.yaml"));
-       assertEquals(Iterables.getFirst(parser.get().keySet(), null), TEST1);
+   @Test
+   public void testDefaultImagePresent() {
+
+      Iterable<Image> defaultImage = Iterables.filter(images.keySet(), new DefaultImagePredicate());
+
+      assertTrue(!Iterables.isEmpty(defaultImage));
+      assertEquals(1, Iterables.size(defaultImage));
    }
 }
\ No newline at end of file
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunningTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunningTest.java
deleted file mode 100644
index 18f7985..0000000
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartJettyIfNotAlreadyRunningTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.jclouds.virtualbox.functions.admin;
-
-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.testng.Assert.assertEquals;
-
-import java.net.URI;
-
-import org.eclipse.jetty.server.Server;
-import org.jclouds.virtualbox.domain.IsoSpec;
-import org.testng.annotations.Test;
-
-/**
- * @author Andrea Turli, Adrian Cole
- */
-@Test(groups = "unit", singleThreaded = true, testName = "StartJettyIfNotAlreadyRunningTest")
-public class StartJettyIfNotAlreadyRunningTest {
-   @Test
-   public void testLoadStartsJettyServer() throws Exception {
-      Server jetty = createMock(Server.class);
-
-      String preconfigurationUrl = "http://foo:8080";
-
-      expect(jetty.getState()).andReturn(Server.STARTED);
-
-      replay(jetty);
-
-      StartJettyIfNotAlreadyRunning starter = new StartJettyIfNotAlreadyRunning(preconfigurationUrl, jetty);
-
-      IsoSpec isoSpec = IsoSpec.builder()
-              .sourcePath("/tmp/myisos/ubuntu.iso")
-              .installationScript("install").build();
-      assertEquals(starter.load(isoSpec), URI.create(preconfigurationUrl));
-      verify(jetty);
-   }
-
-   @Test
-   public void testLaunchJettyServerWhenNotRunningStartsJettyOnCorrectHostPortAndBasedir() {
-      // TODO: all yours!
-   }
-}
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/DefaultImagePredicateTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/DefaultImagePredicateTest.java
new file mode 100644
index 0000000..6c95b27
--- /dev/null
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/DefaultImagePredicateTest.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.virtualbox.predicates;
+
+import static junit.framework.Assert.assertTrue;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_ARCH;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_OS;
+import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_IMAGE_VERSION;
+
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.testng.annotations.Test;
+
+/**
+ * A simple test for {@link DefaultImagePredicate} that makes sure the predicate returns true when
+ * an image built with the defaults is passed and false when it's not.
+ * 
+ * @author dralves
+ * 
+ */
+public class DefaultImagePredicateTest {
+
+   @Test
+   public void testFindDefaultImage() {
+      Image image = new ImageBuilder()
+               .id("test-id")
+               .description("test-image")
+               .operatingSystem(
+                        OperatingSystem.builder().arch(VIRTUALBOX_DEFAULT_IMAGE_ARCH)
+                                 .version(VIRTUALBOX_DEFAULT_IMAGE_VERSION).description("test-os")
+                                 .family(VIRTUALBOX_DEFAULT_IMAGE_OS).build()).build();
+      assertTrue(new DefaultImagePredicate().apply(image));
+   }
+
+   @Test
+   public void testNotFindDefaultImage() {
+      Image image = new ImageBuilder()
+               .id("test-id")
+               .description("test-image")
+               .operatingSystem(
+                        OperatingSystem.builder().arch(VIRTUALBOX_DEFAULT_IMAGE_ARCH)
+                                 .version(VIRTUALBOX_DEFAULT_IMAGE_VERSION).description("test-os")
+                                 .family(OsFamily.UNRECOGNIZED).build()).build();
+      assertTrue(!new DefaultImagePredicate().apply(image));
+   }
+}
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/util/MachineUtilsLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/util/MachineUtilsLiveTest.java
index fba1fcd..007b169 100644
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/util/MachineUtilsLiveTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/util/MachineUtilsLiveTest.java
@@ -25,7 +25,6 @@
 import org.jclouds.config.ValueOfConfigurationKeyOrNull;
 import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
 import org.jclouds.virtualbox.domain.CloneSpec;
-import org.jclouds.virtualbox.domain.ExecutionType;
 import org.jclouds.virtualbox.domain.HardDisk;
 import org.jclouds.virtualbox.domain.IsoSpec;
 import org.jclouds.virtualbox.domain.MasterSpec;
@@ -36,7 +35,6 @@
 import org.jclouds.virtualbox.domain.VmSpec;
 import org.jclouds.virtualbox.functions.CloneAndRegisterMachineFromIMachineIfNotAlreadyExists;
 import org.jclouds.virtualbox.functions.CreateAndInstallVm;
-import org.jclouds.virtualbox.functions.LaunchMachineIfNotAlreadyRunning;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
diff --git a/labs/virtualbox/src/test/resources/default-images.yaml b/labs/virtualbox/src/test/resources/default-images.yaml
new file mode 100644
index 0000000..4efa20d
--- /dev/null
+++ b/labs/virtualbox/src/test/resources/default-images.yaml
@@ -0,0 +1,135 @@
+images:
+    - id: ubuntu-11.04-i386
+      name: ubuntu-11.04-server-i386
+      description: ubuntu 11.04 server (i386)
+      os_arch: x86
+      os_family: ubuntu
+      os_description: ubuntu
+      os_version: 11.04
+      iso: http://releases.ubuntu.com/11.04/ubuntu-11.04-server-i386.iso
+      keystroke_sequence: |
+                <Esc><Esc><Enter> 
+                /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg 
+                debian-installer=en_US auto locale=en_US kbd-chooser/method=us 
+                hostname=vmName 
+                fb=false debconf/frontend=noninteractive 
+                keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false 
+                initrd=/install/initrd.gz -- <Enter>
+      preseed_cfg: |                
+                      ## Options to set on the command line
+                      d-i debian-installer/locale string en_US.utf8
+                      d-i console-setup/ask_detect boolean false
+                      d-i console-setup/layout string USA
+                      d-i netcfg/get_hostname string unassigned-hostname
+                      d-i netcfg/get_domain string unassigned-domain
+                      # Continue without a default route
+                      # Not working , specify a dummy in the DHCP
+                      d-i time/zone string UTC
+                      d-i clock-setup/utc-auto boolean true
+                      d-i clock-setup/utc boolean true
+                      d-i kbd-chooser/method	select	American English
+                      d-i netcfg/wireless_wep string
+                      d-i base-installer/kernel/override-image string linux-server
+                      # Choices: Dialog, Readline, Gnome, Kde, Editor, Noninteractive
+                      d-i debconf debconf/frontend select Noninteractive
+                      d-i pkgsel/install-language-support boolean false
+                      tasksel tasksel/first multiselect standard, ubuntu-server
+                      d-i partman-auto/method string lvm
+                      d-i partman-lvm/confirm boolean true
+                      d-i partman-lvm/device_remove_lvm boolean true
+                      d-i partman-auto/choose_recipe select atomic
+                      d-i partman/confirm_write_new_label boolean true
+                      d-i partman/confirm_nooverwrite boolean true
+                      d-i partman/choose_partition select finish
+                      d-i partman/confirm boolean true
+                      # Write the changes to disks and configure LVM?
+                      d-i partman-lvm/confirm boolean true
+                      d-i partman-lvm/confirm_nooverwrite boolean true
+                      d-i partman-auto-lvm/guided_size string max
+                      ## Default user, we can get away with a recipe to change this
+                      d-i passwd/user-fullname string toor
+                      d-i passwd/username string toor
+                      d-i passwd/user-password password password
+                      d-i passwd/user-password-again password password
+                      d-i user-setup/encrypt-home boolean false
+                      d-i user-setup/allow-password-weak boolean true
+                      # Individual additional packages to install
+                      d-i pkgsel/include string openssh-server ntp
+                      # Whether to upgrade packages after debootstrap.
+                      # Allowed values: none, safe-upgrade, full-upgrade
+                      d-i pkgsel/upgrade select full-upgrade
+                      d-i grub-installer/only_debian boolean true
+                      d-i grub-installer/with_other_os boolean true
+                      d-i finish-install/reboot_in_progress note
+                      #For the update
+                      d-i pkgsel/update-policy select none
+                      # debconf-get-selections --install
+                      #Use mirror
+                      choose-mirror-bin mirror/http/proxy string
+    - id: test-ubuntu-11.10-i386
+      name: ubuntu-11.10-server-i386
+      description: ubuntu 11.10 server (i386)
+      os_arch: x86
+      os_family: ubuntu
+      os_description: ubuntu
+      os_version: 11.10
+      iso: http://releases.ubuntu.com/11.10/ubuntu-11.10-server-i386.iso
+      keystroke_sequence: |
+                <Esc><Esc><Enter> 
+                /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg 
+                debian-installer=en_US auto locale=en_US kbd-chooser/method=us 
+                hostname=vmName 
+                fb=false debconf/frontend=noninteractive 
+                keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false 
+                initrd=/install/initrd.gz -- <Enter>
+      preseed_cfg: |
+                      ## Options to set on the command line
+                      d-i debian-installer/locale string en_US.utf8
+                      d-i console-setup/ask_detect boolean false
+                      d-i console-setup/layout string USA
+                      d-i netcfg/get_hostname string unassigned-hostname
+                      d-i netcfg/get_domain string unassigned-domain
+                      # Continue without a default route
+                      # Not working , specify a dummy in the DHCP
+                      d-i time/zone string UTC
+                      d-i clock-setup/utc-auto boolean true
+                      d-i clock-setup/utc boolean true
+                      d-i kbd-chooser/method    select  American English
+                      d-i netcfg/wireless_wep string
+                      d-i base-installer/kernel/override-image string linux-server
+                      # Choices: Dialog, Readline, Gnome, Kde, Editor, Noninteractive
+                      d-i debconf debconf/frontend select Noninteractive
+                      d-i pkgsel/install-language-support boolean false
+                      tasksel tasksel/first multiselect standard, ubuntu-server
+                      d-i partman-auto/method string lvm
+                      d-i partman-lvm/confirm boolean true
+                      d-i partman-lvm/device_remove_lvm boolean true
+                      d-i partman-auto/choose_recipe select atomic
+                      d-i partman/confirm_write_new_label boolean true
+                      d-i partman/confirm_nooverwrite boolean true
+                      d-i partman/choose_partition select finish
+                      d-i partman/confirm boolean true
+                      # Write the changes to disks and configure LVM?
+                      d-i partman-lvm/confirm boolean true
+                      d-i partman-lvm/confirm_nooverwrite boolean true
+                      d-i partman-auto-lvm/guided_size string max
+                      ## Default user, we can get away with a recipe to change this
+                      d-i passwd/user-fullname string toor
+                      d-i passwd/username string toor
+                      d-i passwd/user-password password password
+                      d-i passwd/user-password-again password password
+                      d-i user-setup/encrypt-home boolean false
+                      d-i user-setup/allow-password-weak boolean true
+                      # Individual additional packages to install
+                      d-i pkgsel/include string openssh-server ntp
+                      # Whether to upgrade packages after debootstrap.
+                      # Allowed values: none, safe-upgrade, full-upgrade
+                      d-i pkgsel/upgrade select full-upgrade
+                      d-i grub-installer/only_debian boolean true
+                      d-i grub-installer/with_other_os boolean true
+                      d-i finish-install/reboot_in_progress note
+                      #For the update
+                      d-i pkgsel/update-policy select none
+                      # debconf-get-selections --install
+                      #Use mirror
+                      choose-mirror-bin mirror/http/proxy string
\ No newline at end of file
diff --git a/providers/aws-cloudwatch/src/main/java/org/jclouds/aws/cloudwatch/AWSCloudWatchProviderMetadata.java b/providers/aws-cloudwatch/src/main/java/org/jclouds/aws/cloudwatch/AWSCloudWatchProviderMetadata.java
index d07c5b6..32031a9 100644
--- a/providers/aws-cloudwatch/src/main/java/org/jclouds/aws/cloudwatch/AWSCloudWatchProviderMetadata.java
+++ b/providers/aws-cloudwatch/src/main/java/org/jclouds/aws/cloudwatch/AWSCloudWatchProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.aws.cloudwatch;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.cloudwatch.CloudWatchApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of @ link org.jclouds.types.ProviderMetadata} for Amazon's CloudWatch
@@ -34,82 +31,38 @@
  */
 public class AWSCloudWatchProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "aws-cloudwatch";
+   public AWSCloudWatchProviderMetadata() {
+      this(builder()
+            .id("aws-cloudwatch")
+            .name("Amazon CloudWatch")
+            .api(new CloudWatchApiMetadata())
+            .homepage(URI.create("http://aws.amazon.com/cloudwatch"))
+            .console(URI.create("https://console.aws.amazon.com/cloudwatch/home"))
+            .linkedServices("aws-ec2","aws-elb", "aws-cloudwatch", "aws-s3", "aws-simpledb")
+            .iso3166Codes("US-VA", "US-CA", "BR-SP", "US-OR", "IE", "SG", "JP-13"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.MONITOR_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AWSCloudWatchProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Amazon CloudWatch";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public AWSCloudWatchProviderMetadata build() {
+         return new AWSCloudWatchProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Access Key ID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret Access Key";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://aws.amazon.com/cloudwatch");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://console.aws.amazon.com/cloudwatch/home");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://docs.amazonwebservices.com/AmazonCloudWatch/latest/APIReference/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("aws-s3", "aws-ec2", "aws-cloudwatch", "aws-simpledb");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-VA", "US-CA", "BR-SP", "US-OR", "IE", "SG", "JP-13");
-   }
 }
diff --git a/providers/aws-cloudwatch/src/test/java/org/jclouds/aws/cloudwatch/AWSCloudWatchProviderTest.java b/providers/aws-cloudwatch/src/test/java/org/jclouds/aws/cloudwatch/AWSCloudWatchProviderTest.java
index 83fb1f3..77882b1 100644
--- a/providers/aws-cloudwatch/src/test/java/org/jclouds/aws/cloudwatch/AWSCloudWatchProviderTest.java
+++ b/providers/aws-cloudwatch/src/test/java/org/jclouds/aws/cloudwatch/AWSCloudWatchProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.aws.cloudwatch;
 
+import org.jclouds.cloudwatch.CloudWatchApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,6 +31,6 @@
 public class AWSCloudWatchProviderTest extends BaseProviderMetadataTest {
 
    public AWSCloudWatchProviderTest() {
-      super(new AWSCloudWatchProviderMetadata(), ProviderMetadata.MONITOR_TYPE);
+      super(new AWSCloudWatchProviderMetadata(), new CloudWatchApiMetadata());
    }
 }
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java
new file mode 100644
index 0000000..04cf5b3
--- /dev/null
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.aws.ec2;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.ec2.EC2ApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the Amazon-specific EC2 API
+ * 
+ * @author Adrian Cole
+ */
+public class AWSEC2ApiMetadata extends EC2ApiMetadata {
+
+   public AWSEC2ApiMetadata() {
+      this(builder().fromApiMetadata(new EC2ApiMetadata())
+            .id("aws-ec2")
+            .name("Amazon-specific EC2 API"));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AWSEC2ApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends EC2ApiMetadataBuilder<ConcreteBuilder> {
+
+      @Override
+      public AWSEC2ApiMetadata build() {
+         return new AWSEC2ApiMetadata(this);
+      }
+   }
+
+   private static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2PropertiesBuilder.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2PropertiesBuilder.java
index 5b125fe..a79f049 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2PropertiesBuilder.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2PropertiesBuilder.java
@@ -18,6 +18,7 @@
  */
 package org.jclouds.aws.ec2;
 
+import static org.jclouds.Constants.PROPERTY_API_VERSION;
 import static org.jclouds.Constants.PROPERTY_ENDPOINT;
 import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_AMI_QUERY;
 import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY;
@@ -39,6 +40,7 @@
    @Override
    protected Properties defaultProperties() {
       Properties properties = super.defaultProperties();
+      properties.setProperty(PROPERTY_API_VERSION, AWSEC2AsyncClient.VERSION);
       // sometimes, like in ec2, stop takes a very long time, perhaps
       // due to volume management. one example spent 2 minutes moving
       // from stopping->stopped state on an ec2 micro
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ProviderMetadata.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ProviderMetadata.java
index b77d829..945c418 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ProviderMetadata.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.aws.ec2;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@ link org.jclouds.types.ProviderMetadata} for Amazon's
@@ -34,83 +30,38 @@
  */
 public class AWSEC2ProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "aws-ec2";
+   public AWSEC2ProviderMetadata() {
+      this(builder()
+            .id("aws-ec2")
+            .name("Amazon Elastic Compute Cloud (EC2)")
+            .api(new AWSEC2ApiMetadata())
+            .homepage(URI.create("http://aws.amazon.com/ec2"))
+            .console(URI.create("https://console.aws.amazon.com/ec2/home"))
+            .linkedServices("aws-ec2","aws-elb", "aws-cloudwatch", "aws-s3", "aws-simpledb")
+            .iso3166Codes("US-VA", "US-CA", "US-OR", "BR-SP", "IE", "SG", "JP-13"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AWSEC2ProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Amazon Elastic Compute Cloud (EC2)";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public AWSEC2ProviderMetadata build() {
+         return new AWSEC2ProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Access Key ID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret Access Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://aws.amazon.com/ec2/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://console.aws.amazon.com/ec2/home");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://docs.amazonwebservices.com/AWSEC2/latest/APIReference");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("aws-s3", "aws-ec2", "aws-elb", "aws-simpledb");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-VA", "US-CA", "US-OR", "BR-SP", "IE", "SG", "JP-13");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
\ No newline at end of file
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
index 07c634a..a59b027 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
@@ -47,9 +47,9 @@
 import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier;
 import org.jclouds.ec2.compute.config.EC2BindComputeStrategiesByClass;
 import org.jclouds.ec2.compute.domain.RegionAndName;
-import org.jclouds.ec2.compute.functions.RegionAndIdToImage;
 import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata;
 import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
+import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
 import org.jclouds.ec2.compute.options.EC2TemplateOptions;
 import org.jclouds.ec2.compute.predicates.InstancePresent;
 import org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions;
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java
index 5c1738e..de2a4e3 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceDependenciesModule.java
@@ -44,12 +44,12 @@
 import org.jclouds.domain.Credentials;
 import org.jclouds.ec2.compute.config.EC2ComputeServiceDependenciesModule;
 import org.jclouds.ec2.compute.domain.RegionAndName;
-import org.jclouds.ec2.compute.functions.CreateSecurityGroupIfNeeded;
 import org.jclouds.ec2.compute.functions.CreateUniqueKeyPair;
 import org.jclouds.ec2.compute.functions.CredentialsForInstance;
-import org.jclouds.ec2.compute.functions.LoadPublicIpForInstanceOrNull;
-import org.jclouds.ec2.compute.functions.RegionAndIdToImage;
 import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl;
+import org.jclouds.ec2.compute.loaders.CreateSecurityGroupIfNeeded;
+import org.jclouds.ec2.compute.loaders.LoadPublicIpForInstanceOrNull;
+import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
 import org.jclouds.ec2.domain.KeyPair;
 import org.jclouds.ec2.domain.RunningInstance;
 import org.jclouds.predicates.RetryablePredicate;
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClient.java
index cdcc825..d194704 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -27,7 +26,6 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 
-import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.xml.ProductCodesHandler;
 import org.jclouds.aws.filters.FormSigner;
 import org.jclouds.ec2.binders.BindProductCodesToIndexedFormParams;
@@ -51,7 +49,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values =  AWSEC2AsyncClient.VERSION)
 @VirtualHost
 public interface AWSAMIAsyncClient extends AMIAsyncClient {
    // TODO make AWSImage as it has product codes...
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSInstanceAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSInstanceAsyncClient.java
index 3162e5b..5a09a40 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSInstanceAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSInstanceAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -27,7 +26,6 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 
-import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.domain.AWSRunningInstance;
 import org.jclouds.aws.ec2.xml.AWSDescribeInstancesResponseHandler;
 import org.jclouds.aws.ec2.xml.AWSRunInstancesResponseHandler;
@@ -57,7 +55,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION)
 @VirtualHost
 public interface AWSInstanceAsyncClient extends InstanceAsyncClient {
 
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java
index fc03d3d..cec9808 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java
@@ -19,13 +19,11 @@
 package org.jclouds.aws.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import javax.ws.rs.FormParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 
-import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.functions.EncodedRSAPublicKeyToBase64;
 import org.jclouds.aws.filters.FormSigner;
 import org.jclouds.ec2.domain.KeyPair;
@@ -47,7 +45,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION)
 @VirtualHost
 public interface AWSKeyPairAsyncClient extends KeyPairAsyncClient {
 
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java
index 217ff12..c98a76a 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -27,7 +26,6 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 
-import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.options.CreateSecurityGroupOptions;
 import org.jclouds.aws.ec2.xml.CreateSecurityGroupResponseHandler;
 import org.jclouds.aws.filters.FormSigner;
@@ -60,7 +58,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION)
 @VirtualHost
 @Beta
 public interface AWSSecurityGroupAsyncClient extends SecurityGroupAsyncClient {
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/MonitoringAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/MonitoringAsyncClient.java
index d296478..e219917 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/MonitoringAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/MonitoringAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Map;
 
@@ -27,7 +26,6 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 
-import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.domain.MonitoringState;
 import org.jclouds.aws.ec2.xml.MonitoringStateHandler;
 import org.jclouds.aws.filters.FormSigner;
@@ -50,7 +48,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION)
 @VirtualHost
 public interface MonitoringAsyncClient {
 
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClient.java
index 2383e8d..9f6084a 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -27,7 +26,6 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 
-import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.domain.PlacementGroup;
 import org.jclouds.aws.ec2.xml.DescribePlacementGroupsResponseHandler;
 import org.jclouds.aws.filters.FormSigner;
@@ -53,7 +51,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION)
 @VirtualHost
 public interface PlacementGroupAsyncClient {
 
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/SpotInstanceAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/SpotInstanceAsyncClient.java
index fc8a919..d65626b 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/SpotInstanceAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/SpotInstanceAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Set;
 
@@ -27,7 +26,6 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 
-import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.binders.BindLaunchSpecificationToFormParams;
 import org.jclouds.aws.ec2.binders.BindSpotInstanceRequestIdsToIndexedFormParams;
 import org.jclouds.aws.ec2.domain.LaunchSpecification;
@@ -60,7 +58,6 @@
  * @author Adrian Cole
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION)
 @VirtualHost
 public interface SpotInstanceAsyncClient {
 
diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java
index 49ceee9..8a4513a 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.ec2.services;
 
 import static org.jclouds.aws.reference.FormParameters.ACTION;
-import static org.jclouds.aws.reference.FormParameters.VERSION;
 
 import java.util.Map;
 import java.util.Set;
@@ -27,7 +26,6 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 
-import org.jclouds.aws.ec2.AWSEC2AsyncClient;
 import org.jclouds.aws.ec2.binders.BindResourceIdsToIndexedFormParams;
 import org.jclouds.aws.ec2.binders.BindTagFiltersToIndexedFormParams;
 import org.jclouds.aws.ec2.binders.BindTagsToIndexedFormParams;
@@ -55,7 +53,6 @@
  * @author grkvlt@apache.org
  */
 @RequestFilters(FormSigner.class)
-@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION)
 @VirtualHost
 public interface TagAsyncClient {
     /**
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2ProviderTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2ProviderTest.java
index 61ecca3..5276ad3 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2ProviderTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2ProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.ec2;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,6 +30,6 @@
 public class AWSEC2ProviderTest extends BaseProviderMetadataTest {
 
    public AWSEC2ProviderTest() {
-      super(new AWSEC2ProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new AWSEC2ProviderMetadata(), new AWSEC2ApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModuleTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModuleTest.java
index ed9db18..10d15d4 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModuleTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModuleTest.java
@@ -25,7 +25,7 @@
 
 import org.jclouds.compute.domain.Image;
 import org.jclouds.ec2.compute.domain.RegionAndName;
-import org.jclouds.ec2.compute.functions.RegionAndIdToImage;
+import org.jclouds.ec2.compute.loaders.RegionAndIdToImage;
 import org.jclouds.rest.AuthorizationException;
 import org.testng.annotations.Test;
 
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClientTest.java
index b19e71b..0d253b6 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClientTest.java
@@ -63,7 +63,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=CreateImage&InstanceId=instanceId&Name=name",
+      assertPayloadEquals(request, "Action=CreateImage&InstanceId=instanceId&Name=name",
             "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ParseSax.class);
       assertSaxResponseParserClassEquals(method, ImageIdHandler.class);
@@ -82,7 +82,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=CreateImage&InstanceId=instanceId&Name=name&Description=description&NoReboot=true",
+            "Action=CreateImage&InstanceId=instanceId&Name=name&Description=description&NoReboot=true",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -99,7 +99,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeImages", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeImages", "application/x-www-form-urlencoded",
             false);
    
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -121,7 +121,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
                request,
-               "Version=2011-05-15&Action=DescribeImages&Filter.1.Name=state&Filter.1.Value.1=available&Filter.2.Name=image-type&Filter.2.Value.1=machine&ExecutableBy=me&Owner.1=fred&Owner.2=nancy&ImageId.1=1&ImageId.2=2",
+               "Action=DescribeImages&Filter.1.Name=state&Filter.1.Value.1=available&Filter.2.Name=image-type&Filter.2.Value.1=machine&ExecutableBy=me&Owner.1=fred&Owner.2=nancy&ImageId.1=1&ImageId.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -137,7 +137,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DeregisterImage&ImageId=imageId",
+      assertPayloadEquals(request, "Action=DeregisterImage&ImageId=imageId",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -154,7 +154,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=RegisterImage&ImageLocation=pathToManifest&Name=name",
+      assertPayloadEquals(request, "Action=RegisterImage&ImageLocation=pathToManifest&Name=name",
             "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ParseSax.class);
       assertSaxResponseParserClassEquals(method, ImageIdHandler.class);
@@ -172,7 +172,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=RegisterImage&ImageLocation=pathToManifest&Name=name&Description=description",
+            "Action=RegisterImage&ImageLocation=pathToManifest&Name=name&Description=description",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -191,7 +191,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=RegisterImage&RootDeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Name=imageName",
+            "Action=RegisterImage&RootDeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Name=imageName",
             "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ParseSax.class);
       assertSaxResponseParserClassEquals(method, ImageIdHandler.class);
@@ -216,7 +216,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=RegisterImage&RootDeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Name=imageName&Description=description&BlockDeviceMapping.1.Ebs.DeleteOnTermination=false&BlockDeviceMapping.1.DeviceName=%2Fdev%2Fdevice&BlockDeviceMapping.1.Ebs.SnapshotId=snapshot&BlockDeviceMapping.2.Ebs.DeleteOnTermination=false&BlockDeviceMapping.2.DeviceName=%2Fdev%2Fnewdevice&BlockDeviceMapping.2.VirtualName=newblock&BlockDeviceMapping.2.Ebs.VolumeSize=100",
+            "Action=RegisterImage&RootDeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.0.Ebs.SnapshotId=snapshotId&Name=imageName&Description=description&BlockDeviceMapping.1.Ebs.DeleteOnTermination=false&BlockDeviceMapping.1.DeviceName=%2Fdev%2Fdevice&BlockDeviceMapping.1.Ebs.SnapshotId=snapshot&BlockDeviceMapping.2.Ebs.DeleteOnTermination=false&BlockDeviceMapping.2.DeviceName=%2Fdev%2Fnewdevice&BlockDeviceMapping.2.VirtualName=newblock&BlockDeviceMapping.2.Ebs.VolumeSize=100",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -233,7 +233,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeImageAttribute&Attribute=productCodes&ImageId=imageId",
+            "Action=DescribeImageAttribute&Attribute=productCodes&ImageId=imageId",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -251,7 +251,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeImageAttribute&Attribute=blockDeviceMapping&ImageId=imageId",
+            "Action=DescribeImageAttribute&Attribute=blockDeviceMapping&ImageId=imageId",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -269,7 +269,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeImageAttribute&Attribute=launchPermission&ImageId=imageId",
+            "Action=DescribeImageAttribute&Attribute=launchPermission&ImageId=imageId",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -289,7 +289,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=ModifyImageAttribute&OperationType=add&Attribute=launchPermission&ImageId=imageId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
+            "Action=ModifyImageAttribute&OperationType=add&Attribute=launchPermission&ImageId=imageId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
             "application/x-www-form-urlencoded", false);
   
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -309,7 +309,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=ModifyImageAttribute&OperationType=remove&Attribute=launchPermission&ImageId=imageId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
+            "Action=ModifyImageAttribute&OperationType=remove&Attribute=launchPermission&ImageId=imageId&UserGroup.1=all&UserId.1=bob&UserId.2=sue",
             "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
@@ -326,7 +326,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=ResetImageAttribute&Attribute=launchPermission&ImageId=imageId",
+            "Action=ResetImageAttribute&Attribute=launchPermission&ImageId=imageId",
             "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
@@ -344,7 +344,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=ModifyImageAttribute&OperationType=add&Attribute=productCodes&ImageId=imageId&ProductCode.1=code1&ProductCode.2=code2",
+            "Action=ModifyImageAttribute&OperationType=add&Attribute=productCodes&ImageId=imageId&ProductCode.1=code1&ProductCode.2=code2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -363,7 +363,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=ModifyImageAttribute&OperationType=remove&Attribute=productCodes&ImageId=imageId&ProductCode.1=code1&ProductCode.2=code2",
+            "Action=ModifyImageAttribute&OperationType=remove&Attribute=productCodes&ImageId=imageId&ProductCode.1=code1&ProductCode.2=code2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
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 49cc347..dd0f9cd 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
@@ -62,7 +62,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeInstances", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeInstances", "application/x-www-form-urlencoded",
             false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -78,7 +78,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeInstances&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=DescribeInstances&InstanceId.1=1&InstanceId.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -95,7 +95,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=TerminateInstances&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=TerminateInstances&InstanceId.1=1&InstanceId.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -113,11 +113,11 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       try {
-         assertPayloadEquals(request, "Version=2011-05-15&Action=RunInstances&ImageId=ami-voo&MinCount=1&MaxCount=1",
+         assertPayloadEquals(request, "Action=RunInstances&ImageId=ami-voo&MinCount=1&MaxCount=1",
                "application/x-www-form-urlencoded", false);
       } catch (AssertionError e) {
          // mvn 3.0 osx 10.6.5 somehow sorts differently
-         assertPayloadEquals(request, "Version=2011-05-15&Action=RunInstances&ImageId=ami-voo&MaxCount=1&MinCount=1",
+         assertPayloadEquals(request, "Action=RunInstances&ImageId=ami-voo&MaxCount=1&MinCount=1",
                "application/x-www-form-urlencoded", false);
       }
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -145,13 +145,13 @@
       try {
          assertPayloadEquals(
                request,
-               "Version=2011-05-15&Action=RunInstances&ImageId=ami-voo&MinCount=1&MaxCount=5&KernelId=kernelId&Monitoring.Enabled=true&SecurityGroup.1=group1&SecurityGroup.2=group2&Placement.AvailabilityZone=us-east-1a",
+               "Action=RunInstances&ImageId=ami-voo&MinCount=1&MaxCount=5&KernelId=kernelId&Monitoring.Enabled=true&SecurityGroup.1=group1&SecurityGroup.2=group2&Placement.AvailabilityZone=us-east-1a",
                "application/x-www-form-urlencoded", false);
       } catch (AssertionError e) {
          // mvn 3.0 osx 10.6.5 somehow sorts differently
          assertPayloadEquals(
                request,
-               "Version=2011-05-15&Action=RunInstances&ImageId=ami-voo&MaxCount=5&MinCount=1&KernelId=kernelId&Monitoring.Enabled=true&SecurityGroup.1=group1&SecurityGroup.2=group2&Placement.AvailabilityZone=us-east-1a",
+               "Action=RunInstances&ImageId=ami-voo&MaxCount=5&MinCount=1&KernelId=kernelId&Monitoring.Enabled=true&SecurityGroup.1=group1&SecurityGroup.2=group2&Placement.AvailabilityZone=us-east-1a",
                "application/x-www-form-urlencoded", false);
       }
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -168,7 +168,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=StopInstances&Force=true&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=StopInstances&Force=true&InstanceId.1=1&InstanceId.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -185,7 +185,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=RebootInstances&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=RebootInstances&InstanceId.1=1&InstanceId.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -202,7 +202,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=StartInstances&InstanceId.1=1&InstanceId.2=2",
+      assertPayloadEquals(request, "Action=StartInstances&InstanceId.1=1&InstanceId.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -220,7 +220,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeInstanceAttribute&Attribute=userData&InstanceId=1",
+            "Action=DescribeInstanceAttribute&Attribute=userData&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -238,7 +238,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeInstanceAttribute&Attribute=rootDeviceName&InstanceId=1",
+            "Action=DescribeInstanceAttribute&Attribute=rootDeviceName&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -256,7 +256,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeInstanceAttribute&Attribute=ramdisk&InstanceId=1",
+            "Action=DescribeInstanceAttribute&Attribute=ramdisk&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -275,7 +275,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeInstanceAttribute&Attribute=disableApiTermination&InstanceId=1",
+            "Action=DescribeInstanceAttribute&Attribute=disableApiTermination&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -292,7 +292,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeInstanceAttribute&Attribute=kernel&InstanceId=1",
+      assertPayloadEquals(request, "Action=DescribeInstanceAttribute&Attribute=kernel&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -310,7 +310,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeInstanceAttribute&Attribute=instanceType&InstanceId=1",
+            "Action=DescribeInstanceAttribute&Attribute=instanceType&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -330,7 +330,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=DescribeInstanceAttribute&Attribute=instanceInitiatedShutdownBehavior&InstanceId=1",
+            "Action=DescribeInstanceAttribute&Attribute=instanceInitiatedShutdownBehavior&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -349,7 +349,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=DescribeInstanceAttribute&Attribute=blockDeviceMapping&InstanceId=1",
+            "Action=DescribeInstanceAttribute&Attribute=blockDeviceMapping&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -367,7 +367,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=ModifyInstanceAttribute&Attribute=userData&Value=dGVzdA%3D%3D&InstanceId=1",
+            "Action=ModifyInstanceAttribute&Attribute=userData&Value=dGVzdA%3D%3D&InstanceId=1",
             "application/x-www-form-urlencoded", false);
       filter.filter(request);// ensure encoding worked properly
       assertPayloadEquals(
@@ -389,7 +389,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=ModifyInstanceAttribute&Attribute=ramdisk&Value=test&InstanceId=1",
+            "Action=ModifyInstanceAttribute&Attribute=ramdisk&Value=test&InstanceId=1",
             "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
@@ -406,7 +406,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=ModifyInstanceAttribute&Attribute=kernel&Value=test&InstanceId=1",
+            "Action=ModifyInstanceAttribute&Attribute=kernel&Value=test&InstanceId=1",
             "application/x-www-form-urlencoded", false);
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
       assertSaxResponseParserClassEquals(method, null);
@@ -425,7 +425,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=ModifyInstanceAttribute&Attribute=disableApiTermination&Value=true&InstanceId=1",
+            "Action=ModifyInstanceAttribute&Attribute=disableApiTermination&Value=true&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -443,7 +443,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=ModifyInstanceAttribute&Attribute=instanceType&Value=c1.medium&InstanceId=1",
+            "Action=ModifyInstanceAttribute&Attribute=instanceType&Value=c1.medium&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -463,7 +463,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=ModifyInstanceAttribute&Attribute=instanceInitiatedShutdownBehavior&Value=terminate&InstanceId=1",
+            "Action=ModifyInstanceAttribute&Attribute=instanceInitiatedShutdownBehavior&Value=terminate&InstanceId=1",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -486,7 +486,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=ModifyInstanceAttribute&InstanceId=1&BlockDeviceMapping.1.Ebs.VolumeId=vol-test1&BlockDeviceMapping.1.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.1.Ebs.DeleteOnTermination=true",
+            "Action=ModifyInstanceAttribute&InstanceId=1&BlockDeviceMapping.1.Ebs.VolumeId=vol-test1&BlockDeviceMapping.1.DeviceName=%2Fdev%2Fsda1&BlockDeviceMapping.1.Ebs.DeleteOnTermination=true",
             "application/x-www-form-urlencoded", false);
       filter.filter(request);// ensure encoding worked properly
       assertPayloadEquals(
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClientTest.java
index 45eb6c5..aea88ea 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClientTest.java
@@ -48,7 +48,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=CreateKeyPair&KeyName=mykey",
+      assertPayloadEquals(request, "Action=CreateKeyPair&KeyName=mykey",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -65,7 +65,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=ImportKeyPair&PublicKeyMaterial=c3NoLXJzYSBBQQ%3D%3D&KeyName=mykey",
+      assertPayloadEquals(request, "Action=ImportKeyPair&PublicKeyMaterial=c3NoLXJzYSBBQQ%3D%3D&KeyName=mykey",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -81,7 +81,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DeleteKeyPair&KeyName=mykey",
+      assertPayloadEquals(request, "Action=DeleteKeyPair&KeyName=mykey",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -98,7 +98,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeKeyPairs", "application/x-www-form-urlencoded",
+      assertPayloadEquals(request, "Action=DescribeKeyPairs", "application/x-www-form-urlencoded",
             false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -115,7 +115,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeKeyPairs&KeyName.1=1&KeyName.2=2",
+      assertPayloadEquals(request, "Action=DescribeKeyPairs&KeyName.1=1&KeyName.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java
index aca85b9..35e0489 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSSecurityGroupAsyncClientTest.java
@@ -58,7 +58,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DeleteSecurityGroup&GroupId=id",
+      assertPayloadEquals(request, "Action=DeleteSecurityGroup&GroupId=id",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -75,7 +75,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeSecurityGroups",
+      assertPayloadEquals(request, "Action=DescribeSecurityGroups",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -92,7 +92,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeSecurityGroups&GroupId.1=1&GroupId.2=2",
+      assertPayloadEquals(request, "Action=DescribeSecurityGroups&GroupId.1=1&GroupId.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -110,7 +110,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=CreateSecurityGroup&GroupDescription=description&GroupName=name",
+            "Action=CreateSecurityGroup&GroupDescription=description&GroupName=name",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -129,7 +129,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=AuthorizeSecurityGroupIngress&GroupId=group&IpPermissions.0.IpProtocol=-1&IpPermissions.0.FromPort=1&IpPermissions.0.ToPort=65535&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0%2F0",
+            "Action=AuthorizeSecurityGroupIngress&GroupId=group&IpPermissions.0.IpProtocol=-1&IpPermissions.0.FromPort=1&IpPermissions.0.ToPort=65535&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0%2F0",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -150,7 +150,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=AuthorizeSecurityGroupIngress&GroupId=group&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=1&IpPermissions.0.ToPort=65535&IpPermissions.0.IpRanges.0.CidrIp=1.1.1.1%2F32&IpPermissions.1.IpProtocol=icmp&IpPermissions.1.FromPort=8&IpPermissions.1.ToPort=0&IpPermissions.1.Groups.0.GroupId=groupId",
+            "Action=AuthorizeSecurityGroupIngress&GroupId=group&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=1&IpPermissions.0.ToPort=65535&IpPermissions.0.IpRanges.0.CidrIp=1.1.1.1%2F32&IpPermissions.1.IpProtocol=icmp&IpPermissions.1.FromPort=8&IpPermissions.1.ToPort=0&IpPermissions.1.Groups.0.GroupId=groupId",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -169,7 +169,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=RevokeSecurityGroupIngress&GroupId=group&IpPermissions.0.IpProtocol=-1&IpPermissions.0.FromPort=1&IpPermissions.0.ToPort=65535&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0%2F0",
+            "Action=RevokeSecurityGroupIngress&GroupId=group&IpPermissions.0.IpProtocol=-1&IpPermissions.0.FromPort=1&IpPermissions.0.ToPort=65535&IpPermissions.0.IpRanges.0.CidrIp=0.0.0.0%2F0",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -190,7 +190,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=RevokeSecurityGroupIngress&GroupId=group&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=1&IpPermissions.0.ToPort=65535&IpPermissions.0.IpRanges.0.CidrIp=1.1.1.1%2F32&IpPermissions.1.IpProtocol=icmp&IpPermissions.1.FromPort=8&IpPermissions.1.ToPort=0&IpPermissions.1.Groups.0.GroupId=groupId",
+            "Action=RevokeSecurityGroupIngress&GroupId=group&IpPermissions.0.IpProtocol=tcp&IpPermissions.0.FromPort=1&IpPermissions.0.ToPort=65535&IpPermissions.0.IpRanges.0.CidrIp=1.1.1.1%2F32&IpPermissions.1.IpProtocol=icmp&IpPermissions.1.FromPort=8&IpPermissions.1.ToPort=0&IpPermissions.1.Groups.0.GroupId=groupId",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringAsyncClientTest.java
index 3165fd5..f9de8fa 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringAsyncClientTest.java
@@ -45,7 +45,7 @@
       HttpRequest request = processor.createRequest(method, null, "instance1", "instance2");
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
-      String payload = "Version=2011-05-15&Action=UnmonitorInstances&InstanceId.0=instance1&InstanceId.1=instance2";
+      String payload = "Action=UnmonitorInstances&InstanceId.0=instance1&InstanceId.1=instance2";
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request, payload, "application/x-www-form-urlencoded", false);
 
@@ -64,7 +64,7 @@
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(request,
-            "Version=2011-05-15&Action=MonitorInstances&InstanceId.0=instance1&InstanceId.1=instance2",
+            "Action=MonitorInstances&InstanceId.0=instance1&InstanceId.1=instance2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClientTest.java
index f5d66f1..94a4a92 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClientTest.java
@@ -48,7 +48,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DeletePlacementGroup&GroupName=name",
+      assertPayloadEquals(request, "Action=DeletePlacementGroup&GroupName=name",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -65,7 +65,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=CreatePlacementGroup&Strategy=cluster&GroupName=name",
+      assertPayloadEquals(request, "Action=CreatePlacementGroup&Strategy=cluster&GroupName=name",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -82,7 +82,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=CreatePlacementGroup&Strategy=cluster&GroupName=name",
+      assertPayloadEquals(request, "Action=CreatePlacementGroup&Strategy=cluster&GroupName=name",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -99,7 +99,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribePlacementGroups",
+      assertPayloadEquals(request, "Action=DescribePlacementGroups",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -116,7 +116,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribePlacementGroups&GroupName.1=1&GroupName.2=2",
+      assertPayloadEquals(request, "Action=DescribePlacementGroups&GroupName.1=1&GroupName.2=2",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/SpotInstanceAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/SpotInstanceAsyncClientTest.java
index 79a9ce2..37d5af2 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/SpotInstanceAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/SpotInstanceAsyncClientTest.java
@@ -55,7 +55,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=RequestSpotInstances&LaunchSpecification.ImageId=m1.small&SpotPrice=0.01&LaunchSpecification.InstanceType=ami-voo",
+            "Action=RequestSpotInstances&LaunchSpecification.ImageId=m1.small&SpotPrice=0.01&LaunchSpecification.InstanceType=ami-voo",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -77,7 +77,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.eu-west-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=RequestSpotInstances&InstanceCount=3&SpotPrice=0.01&ValidFrom=1970-05-23T21%3A21%3A18Z&ValidUntil=2009-02-13T23%3A31%3A31Z&AvailabilityZoneGroup=availabilityZoneGroup&LaunchGroup=launchGroup&LaunchSpecification.ImageId=ami-voo&LaunchSpecification.Placement.AvailabilityZone=eu-west-1a&LaunchSpecification.SecurityGroup.1=group1&LaunchSpecification.InstanceType=m1.small&LaunchSpecification.KernelId=kernelId",
+            "Action=RequestSpotInstances&InstanceCount=3&SpotPrice=0.01&ValidFrom=1970-05-23T21%3A21%3A18Z&ValidUntil=2009-02-13T23%3A31%3A31Z&AvailabilityZoneGroup=availabilityZoneGroup&LaunchGroup=launchGroup&LaunchSpecification.ImageId=ami-voo&LaunchSpecification.Placement.AvailabilityZone=eu-west-1a&LaunchSpecification.SecurityGroup.1=group1&LaunchSpecification.InstanceType=m1.small&LaunchSpecification.KernelId=kernelId",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -94,7 +94,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=CancelSpotInstanceRequests&SpotInstanceRequestId.1=id",
+      assertPayloadEquals(request, "Action=CancelSpotInstanceRequests&SpotInstanceRequestId.1=id",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -111,7 +111,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeSpotInstanceRequests",
+      assertPayloadEquals(request, "Action=DescribeSpotInstanceRequests",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -130,7 +130,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=DescribeSpotInstanceRequests&SpotInstanceRequestId.1=1&SpotInstanceRequestId.2=2",
+            "Action=DescribeSpotInstanceRequests&SpotInstanceRequestId.1=1&SpotInstanceRequestId.2=2",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -147,7 +147,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeSpotPriceHistory",
+      assertPayloadEquals(request, "Action=DescribeSpotPriceHistory",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -170,7 +170,7 @@
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
       assertPayloadEquals(
             request,
-            "Version=2011-05-15&Action=DescribeSpotPriceHistory&StartTime=1970-05-23T21%3A21%3A18.910Z&EndTime=2009-02-13T23%3A31%3A31.011Z&ProductDescription=description&InstanceType.1=m1.small",
+            "Action=DescribeSpotPriceHistory&StartTime=1970-05-23T21%3A21%3A18.910Z&EndTime=2009-02-13T23%3A31%3A31.011Z&ProductDescription=description&InstanceType.1=m1.small",
             "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java
index 4b0292f..33dd784 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java
@@ -50,7 +50,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DeleteTags&Tag.1.Key=yyy&Tag.1.Value=zzz&ResourceId.1=xxx",
+      assertPayloadEquals(request, "Action=DeleteTags&Tag.1.Key=yyy&Tag.1.Value=zzz&ResourceId.1=xxx",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -66,7 +66,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=CreateTags&Tag.1.Key=yyy&Tag.1.Value=zzz&ResourceId.1=xxx",
+      assertPayloadEquals(request, "Action=CreateTags&Tag.1.Key=yyy&Tag.1.Value=zzz&ResourceId.1=xxx",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
@@ -82,7 +82,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeTags",
+      assertPayloadEquals(request, "Action=DescribeTags",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
@@ -98,7 +98,7 @@
 
       assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1");
       assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n");
-      assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeTags&Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two",
+      assertPayloadEquals(request, "Action=DescribeTags&Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two",
                "application/x-www-form-urlencoded", false);
 
       assertResponseParserClassEquals(method, request, ParseSax.class);
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ApiMetadata.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ApiMetadata.java
new file mode 100644
index 0000000..0b53e4b
--- /dev/null
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ApiMetadata.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.aws.s3;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.s3.S3ApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the Amazon-specific S3 API
+ * 
+ * @author Adrian Cole
+ */
+public class AWSS3ApiMetadata extends S3ApiMetadata {
+
+   public AWSS3ApiMetadata() {
+      this(builder().fromApiMetadata(new S3ApiMetadata())
+            .id("aws-s3")
+            .name("Amazon-specific S3 API"));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AWSS3ApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends S3ApiMetadataBuilder<ConcreteBuilder> {
+
+      @Override
+      public AWSS3ApiMetadata build() {
+         return new AWSS3ApiMetadata(this);
+      }
+   }
+
+   private static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ProviderMetadata.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ProviderMetadata.java
index 57c8a7d..a6b758b 100644
--- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ProviderMetadata.java
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.aws.s3;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.providers.ProviderMetadata} for Amazon's Simple Storage Service
@@ -34,84 +30,38 @@
  */
 public class AWSS3ProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "aws-s3";
+   public AWSS3ProviderMetadata() {
+      this(builder()
+            .id("aws-s3")
+            .name("Amazon Simple Storage Service (S3)")
+            .api(new AWSS3ApiMetadata())
+            .homepage(URI.create("http://aws.amazon.com/s3"))
+            .console(URI.create("https://console.aws.amazon.com/s3/home"))
+            .linkedServices("aws-ec2","aws-elb", "aws-cloudwatch", "aws-s3", "aws-simpledb")
+            .iso3166Codes("US", "US-CA", "US-OR", "BR-SP", "IE", "SG", "JP-13"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.BLOBSTORE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AWSS3ProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Amazon Simple Storage Service (S3)";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public AWSS3ProviderMetadata build() {
+         return new AWSS3ProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Access Key ID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret Access Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://aws.amazon.com/s3/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://console.aws.amazon.com/s3/home");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://docs.amazonwebservices.com/AmazonS3/latest/API");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("aws-s3", "aws-ec2", "aws-elb", "aws-simpledb");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US", "US-CA", "US-OR", "BR-SP", "IE", "SG", "JP-13");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
\ No newline at end of file
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ProviderTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ProviderTest.java
index 3935078..a4fb7bf 100644
--- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ProviderTest.java
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.aws.s3;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +30,7 @@
 public class AWSS3ProviderTest extends BaseProviderMetadataTest {
 
    public AWSS3ProviderTest() {
-      super(new AWSS3ProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new AWSS3ProviderMetadata(), new AWSS3ApiMetadata());
    }
 
 }
\ No newline at end of file
diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobApiMetadata.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobApiMetadata.java
new file mode 100644
index 0000000..3c2e3bd
--- /dev/null
+++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.azureblob;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Microsoft Azure Blob Service API
+ * 
+ * @author Adrian Cole
+ */
+public class AzureBlobApiMetadata extends BaseApiMetadata {
+
+   public AzureBlobApiMetadata() {
+      this(builder()
+            .id("azureblob")
+            .type(ApiType.BLOBSTORE)
+            .name("Microsoft Azure Blob Service API")
+            .identityName("Account Name")
+            .credentialName("Access Key")
+            .documentation(URI.create("http://msdn.microsoft.com/en-us/library/dd135733.aspx")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AzureBlobApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public AzureBlobApiMetadata build() {
+         return new AzureBlobApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobProviderMetadata.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobProviderMetadata.java
index 8838e28..c57e8be 100644
--- a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobProviderMetadata.java
+++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.azureblob;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Microsoft Azure Blob Service.
@@ -32,85 +28,37 @@
  * @author Adrian Cole
  */
 public class AzureBlobProviderMetadata extends BaseProviderMetadata {
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "azureblob";
+   public AzureBlobProviderMetadata() {
+      this(builder()
+            .id("azureblob")
+            .name("Microsoft Azure Blob Service")
+            .api(new AzureBlobApiMetadata())
+            .homepage(URI.create("http://www.microsoft.com/windowsazure/storage/"))
+            .console(URI.create("https://windows.azure.com/default.aspx"))
+            .linkedServices("azureblob", "azurequeue", "azuretable")
+            .iso3166Codes("US-TX","US-IL","IE-D","SG","NL-NH","HK"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.BLOBSTORE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected AzureBlobProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Microsoft Azure Blob Service";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public AzureBlobProviderMetadata build() {
+         return new AzureBlobProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Account Name";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Access Key";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.microsoft.com/windowsazure/storage/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://windows.azure.com/default.aspx");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://msdn.microsoft.com/en-us/library/dd135733.aspx");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("azureblob", "azurequeue", "azuretable");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-TX","US-IL","IE-D","SG","NL-NH","HK");
-   }
-
 }
\ No newline at end of file
diff --git a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobProviderTest.java b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobProviderTest.java
index 71c07ab..d37a651 100644
--- a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobProviderTest.java
+++ b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.azureblob;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +29,6 @@
 public class AzureBlobProviderTest extends BaseProviderMetadataTest {
 
    public AzureBlobProviderTest() {
-      super(new AzureBlobProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new AzureBlobProviderMetadata(), new AzureBlobApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/bluelock-vcloud-zone01/src/main/java/org/jclouds/bluelock/vcloud/zone01/BluelockVCloudZone01ProviderMetadata.java b/providers/bluelock-vcloud-zone01/src/main/java/org/jclouds/bluelock/vcloud/zone01/BluelockVCloudZone01ProviderMetadata.java
index 5124d84..ad88c48 100644
--- a/providers/bluelock-vcloud-zone01/src/main/java/org/jclouds/bluelock/vcloud/zone01/BluelockVCloudZone01ProviderMetadata.java
+++ b/providers/bluelock-vcloud-zone01/src/main/java/org/jclouds/bluelock/vcloud/zone01/BluelockVCloudZone01ProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.bluelock.vcloud.zone01;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.vcloud.VCloudApiMetadata;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Bluelock vCloud Zone 1.
@@ -33,84 +30,37 @@
  */
 public class BluelockVCloudZone01ProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "bluelock-vcloud-zone01";
+   public BluelockVCloudZone01ProviderMetadata() {
+      this(builder()
+            .id("bluelock-vcloud-zone01")
+            .name("Bluelock vCloud Zone 1")
+            .api(new VCloudApiMetadata())
+            .homepage(URI.create("http://www.bluelock.com/bluelock-cloud-hosting"))
+            .console(URI.create("https://zone01.bluelock.com/cloud/org/YOUR_ORG_HERE"))
+            .iso3166Codes("US-IN"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected BluelockVCloudZone01ProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Bluelock vCloud Zone 1";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public BluelockVCloudZone01ProviderMetadata build() {
+         return new BluelockVCloudZone01ProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "User at Organization (user@org)";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Password";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.bluelock.com/bluelock-cloud-hosting");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://zone01.bluelock.com/cloud/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.vmware.com/support/pubs/vcd_pubs.html");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("bluelock-vcloud-zone01");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-IN");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/bluelock-vcloud-zone01/src/test/java/org/jclouds/bluelock/vcloud/zone01/BluelockVCloudZone01ProviderTest.java b/providers/bluelock-vcloud-zone01/src/test/java/org/jclouds/bluelock/vcloud/zone01/BluelockVCloudZone01ProviderTest.java
index b0f8f31..589c135 100644
--- a/providers/bluelock-vcloud-zone01/src/test/java/org/jclouds/bluelock/vcloud/zone01/BluelockVCloudZone01ProviderTest.java
+++ b/providers/bluelock-vcloud-zone01/src/test/java/org/jclouds/bluelock/vcloud/zone01/BluelockVCloudZone01ProviderTest.java
@@ -19,7 +19,7 @@
 package org.jclouds.bluelock.vcloud.zone01;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.vcloud.VCloudApiMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class BluelockVCloudZone01ProviderTest extends BaseProviderMetadataTest {
 
    public BluelockVCloudZone01ProviderTest() {
-      super(new BluelockVCloudZone01ProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new BluelockVCloudZone01ProviderMetadata(), new VCloudApiMetadata());
    }
 }
diff --git a/providers/cloudfiles-uk/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesUKProviderMetadata.java b/providers/cloudfiles-uk/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesUKProviderMetadata.java
index 1068fc6..f50f10a 100644
--- a/providers/cloudfiles-uk/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesUKProviderMetadata.java
+++ b/providers/cloudfiles-uk/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesUKProviderMetadata.java
@@ -19,65 +19,49 @@
 package org.jclouds.rackspace.cloudfiles;
 
 import java.net.URI;
-import java.util.Set;
 
-import org.jclouds.cloudfiles.CloudFilesProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudfiles.CloudFilesApiMetadata;
+import org.jclouds.providers.BaseProviderMetadata;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud Files in UK.
  * 
  * @author Adrian Cole
  */
-public class CloudFilesUKProviderMetadata extends CloudFilesProviderMetadata {
+public class CloudFilesUKProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudfiles-uk";
+   public CloudFilesUKProviderMetadata() {
+      this(builder()
+            .id("cloudfiles-uk")
+            .name("Rackspace Cloud Files UK")
+            .api(new CloudFilesApiMetadata())
+            .homepage(URI.create("http://www.rackspace.co.uk/cloud-hosting/cloud-products/cloud-files"))
+            .console(URI.create("https://lon.manage.rackspacecloud.com"))
+            .linkedServices("cloudfiles-uk", "cloudservers-uk", "cloudloadbalancers-uk")
+            .iso3166Codes("GB-SLG"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Rackspace Cloud Files UK";
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudFilesUKProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.rackspace.co.uk/cloud-hosting/cloud-products/cloud-files");
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudFilesUKProviderMetadata build() {
+         return new CloudFilesUKProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://lon.manage.rackspacecloud.com");
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("cloudfiles-uk", "cloudservers-uk", "cloudloadbalancers-uk");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("GB-SLG");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
\ No newline at end of file
diff --git a/providers/cloudfiles-uk/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesUKProviderTest.java b/providers/cloudfiles-uk/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesUKProviderTest.java
index be2ed86..b8b2b83 100644
--- a/providers/cloudfiles-uk/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesUKProviderTest.java
+++ b/providers/cloudfiles-uk/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesUKProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.rackspace.cloudfiles;
 
+import org.jclouds.cloudfiles.CloudFilesApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class CloudFilesUKProviderTest extends BaseProviderMetadataTest {
 
    public CloudFilesUKProviderTest() {
-      super(new CloudFilesUKProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new CloudFilesUKProviderMetadata(), new CloudFilesApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/cloudfiles-us/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesUSProviderMetadata.java b/providers/cloudfiles-us/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesUSProviderMetadata.java
index e4833dd..c30dad5 100644
--- a/providers/cloudfiles-us/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesUSProviderMetadata.java
+++ b/providers/cloudfiles-us/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesUSProviderMetadata.java
@@ -19,11 +19,9 @@
 package org.jclouds.rackspace.cloudfiles;
 
 import java.net.URI;
-import java.util.Set;
 
-import org.jclouds.cloudfiles.CloudFilesProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudfiles.CloudFilesApiMetadata;
+import org.jclouds.providers.BaseProviderMetadata;
 
 
 /**
@@ -31,54 +29,39 @@
  * 
  * @author Adrian Cole
  */
-public class CloudFilesUSProviderMetadata extends CloudFilesProviderMetadata {
+public class CloudFilesUSProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudfiles-us";
+   public CloudFilesUSProviderMetadata() {
+      this(builder()
+            .id("cloudfiles-us")
+            .name("Rackspace Cloud Files US")
+            .api(new CloudFilesApiMetadata())
+            .homepage(URI.create("http://www.rackspace.com/cloud/cloud_hosting_products/files"))
+            .console(URI.create("https://manage.rackspacecloud.com"))
+            .linkedServices("cloudfiles-us", "cloudservers-us", "cloudloadbalancers-us")
+            .iso3166Codes("US-IL","US-TX"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Rackspace Cloud Files US";
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudFilesUSProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.rackspace.com/cloud/cloud_hosting_products/files");
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudFilesUSProviderMetadata build() {
+         return new CloudFilesUSProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://manage.rackspacecloud.com");
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("cloudfiles-us", "cloudservers-us", "cloudloadbalancers-us");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-IL","US-TX");
-   }
-
 }
\ No newline at end of file
diff --git a/providers/cloudfiles-us/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesUSProviderTest.java b/providers/cloudfiles-us/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesUSProviderTest.java
index bb2c4cb..54c8da6 100644
--- a/providers/cloudfiles-us/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesUSProviderTest.java
+++ b/providers/cloudfiles-us/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesUSProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.rackspace.cloudfiles;
 
+import org.jclouds.cloudfiles.CloudFilesApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class CloudFilesUSProviderTest extends BaseProviderMetadataTest {
 
    public CloudFilesUSProviderTest() {
-      super(new CloudFilesUSProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new CloudFilesUSProviderMetadata(), new CloudFilesApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/cloudloadbalancers-uk/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUKProviderMetadata.java b/providers/cloudloadbalancers-uk/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUKProviderMetadata.java
index 7143076..6069979 100644
--- a/providers/cloudloadbalancers-uk/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUKProviderMetadata.java
+++ b/providers/cloudloadbalancers-uk/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUKProviderMetadata.java
@@ -19,65 +19,48 @@
 package org.jclouds.rackspace.cloudloadbalancers;
 
 import java.net.URI;
-import java.util.Set;
 
-import org.jclouds.cloudloadbalancers.CloudLoadBalancersProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudloadbalancers.CloudLoadBalancersApiMetadata;
+import org.jclouds.providers.BaseProviderMetadata;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud LoadBalancers in UK.
  * 
  * @author Dan Lo Bianco
  */
-public class CloudLoadBalancersUKProviderMetadata extends CloudLoadBalancersProviderMetadata {
+public class CloudLoadBalancersUKProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudloadbalancers-uk";
+   public CloudLoadBalancersUKProviderMetadata() {
+      this(builder()
+            .id("cloudloadbalancers-uk")
+            .name("Rackspace Cloud Load Balancers UK")
+            .api(new CloudLoadBalancersApiMetadata())
+            .homepage(URI.create("http://www.rackspace.co.uk/cloud-hosting/cloud-products/cloud-load-balancers"))
+            .console(URI.create("https://lon.manage.rackspacecloud.com"))
+            .linkedServices("cloudloadbalancers-uk", "cloudservers-uk", "cloudfiles-uk")
+            .iso3166Codes("GB-SLG"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Rackspace Cloud Load Balancers UK";
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.rackspace.co.uk/cloud-hosting/cloud-products/cloud-load-balancers/");
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudLoadBalancersUKProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://lon.manage.rackspacecloud.com");
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudLoadBalancersUKProviderMetadata build() {
+         return new CloudLoadBalancersUKProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("cloudfiles-uk", "cloudservers-uk", "cloudloadbalancers-uk");
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("GB-SLG");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
 }
\ No newline at end of file
diff --git a/providers/cloudloadbalancers-uk/src/test/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUKProviderTest.java b/providers/cloudloadbalancers-uk/src/test/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUKProviderTest.java
index 6d303f4..ec189cf 100644
--- a/providers/cloudloadbalancers-uk/src/test/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUKProviderTest.java
+++ b/providers/cloudloadbalancers-uk/src/test/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUKProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.rackspace.cloudloadbalancers;
 
+import org.jclouds.cloudloadbalancers.CloudLoadBalancersApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class CloudLoadBalancersUKProviderTest extends BaseProviderMetadataTest {
 
    public CloudLoadBalancersUKProviderTest() {
-      super(new CloudLoadBalancersUKProviderMetadata(), ProviderMetadata.LOADBALANCER_TYPE);
+      super(new CloudLoadBalancersUKProviderMetadata(), new CloudLoadBalancersApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUSProviderMetadata.java b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUSProviderMetadata.java
index 1b266aa..4249495 100644
--- a/providers/cloudloadbalancers-us/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUSProviderMetadata.java
+++ b/providers/cloudloadbalancers-us/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUSProviderMetadata.java
@@ -19,65 +19,48 @@
 package org.jclouds.rackspace.cloudloadbalancers;
 
 import java.net.URI;
-import java.util.Set;
 
-import org.jclouds.cloudloadbalancers.CloudLoadBalancersProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudloadbalancers.CloudLoadBalancersApiMetadata;
+import org.jclouds.providers.BaseProviderMetadata;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud LoadBalancers in US.
  * 
  * @author Adrian Cole
  */
-public class CloudLoadBalancersUSProviderMetadata extends CloudLoadBalancersProviderMetadata {
+public class CloudLoadBalancersUSProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudloadbalancers-us";
+   public CloudLoadBalancersUSProviderMetadata() {
+      this(builder()
+            .id("cloudloadbalancers-us")
+            .name("Rackspace Cloud Load Balancers US")
+            .api(new CloudLoadBalancersApiMetadata())
+            .homepage(URI.create("http://www.rackspace.com/cloud/cloud_hosting_products/loadbalancers"))
+            .console(URI.create("https://manage.rackspacecloud.com"))
+            .linkedServices("cloudloadbalancers-us", "cloudservers-us", "cloudfiles-us")
+            .iso3166Codes("US-IL","US-TX"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Rackspace Cloud Load Balancers US";
-   }
-   
-   /**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public URI getHomepage() {
-		return URI.create("http://www.rackspace.com/cloud/cloud_hosting_products/loadbalancers");
-	}
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://manage.rackspacecloud.com");
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudLoadBalancersUSProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("cloudfiles-us", "cloudservers-us", "cloudloadbalancers-us");
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudLoadBalancersUSProviderMetadata build() {
+         return new CloudLoadBalancersUSProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-IL","US-TX");
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
 }
\ No newline at end of file
diff --git a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUSProviderTest.java b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUSProviderTest.java
index 9c28808..7fdf743 100644
--- a/providers/cloudloadbalancers-us/src/test/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUSProviderTest.java
+++ b/providers/cloudloadbalancers-us/src/test/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersUSProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.rackspace.cloudloadbalancers;
 
+import org.jclouds.cloudloadbalancers.CloudLoadBalancersApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class CloudLoadBalancersUSProviderTest extends BaseProviderMetadataTest {
 
    public CloudLoadBalancersUSProviderTest() {
-      super(new CloudLoadBalancersUSProviderMetadata(), ProviderMetadata.LOADBALANCER_TYPE);
+      super(new CloudLoadBalancersUSProviderMetadata(), new CloudLoadBalancersApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/cloudonestorage/src/main/java/org/jclouds/cloudonestorage/CloudOneStorageProviderMetadata.java b/providers/cloudonestorage/src/main/java/org/jclouds/cloudonestorage/CloudOneStorageProviderMetadata.java
index 4704760..01c6e55 100644
--- a/providers/cloudonestorage/src/main/java/org/jclouds/cloudonestorage/CloudOneStorageProviderMetadata.java
+++ b/providers/cloudonestorage/src/main/java/org/jclouds/cloudonestorage/CloudOneStorageProviderMetadata.java
@@ -19,12 +19,10 @@
 package org.jclouds.cloudonestorage;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.atmos.AtmosApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
 
-import com.google.common.collect.ImmutableSet;
-
 /**
  * Implementation of {@ link org.jclouds.types.ProviderMetadata} for PEER1's
  * CloudOne Storage provider.
@@ -33,76 +31,36 @@
  */
 public class CloudOneStorageProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudonestorage";
+   public CloudOneStorageProviderMetadata() {
+      this(builder()
+            .id("cloudonestorage")
+            .name("PEER1 CloudOne Storage")
+            .api(new AtmosApiMetadata())
+            .homepage(URI.create("http://www.peer1.com/hosting/cloudone-storage.php"))
+            .console(URI.create("https://mypeer1.com/"))
+            .iso3166Codes("US-GA", "US-TX"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return BLOBSTORE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudOneStorageProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "PEER1 CloudOne Storage";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudOneStorageProviderMetadata build() {
+         return new CloudOneStorageProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Subtenant ID (UID)";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Shared Secret";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.peer1.com/hosting/cloudone-storage.php");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://mypeer1.com/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.peer1.com/sites/default/files/pdf/Atmos_System_Management_API_Guide_1.3.0A.pdf");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-GA", "US-TX");
-   }
-
 }
diff --git a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/CloudOneStorageProviderTest.java b/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/CloudOneStorageProviderTest.java
index 30e5372..0b1a45b 100644
--- a/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/CloudOneStorageProviderTest.java
+++ b/providers/cloudonestorage/src/test/java/org/jclouds/cloudonestorage/CloudOneStorageProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.cloudonestorage;
 
+import org.jclouds.atmos.AtmosApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +31,7 @@
 public class CloudOneStorageProviderTest extends BaseProviderMetadataTest {
 
    public CloudOneStorageProviderTest() {
-      super(new CloudOneStorageProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new CloudOneStorageProviderMetadata(), new AtmosApiMetadata());
    }
 
 }
\ No newline at end of file
diff --git a/providers/cloudservers-uk/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersUKProviderMetadata.java b/providers/cloudservers-uk/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersUKProviderMetadata.java
index 978b0f6..a2dee8a 100644
--- a/providers/cloudservers-uk/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersUKProviderMetadata.java
+++ b/providers/cloudservers-uk/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersUKProviderMetadata.java
@@ -19,65 +19,49 @@
 package org.jclouds.rackspace.cloudservers;
 
 import java.net.URI;
-import java.util.Set;
 
-import org.jclouds.cloudservers.CloudServersProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudservers.CloudServersApiMetadata;
+import org.jclouds.providers.BaseProviderMetadata;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Rackspace Cloud Servers in UK.
  * 
  * @author Adrian Cole
  */
-public class CloudServersUKProviderMetadata extends CloudServersProviderMetadata {
+public class CloudServersUKProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudservers-uk";
+   public CloudServersUKProviderMetadata() {
+      this(builder()
+            .id("cloudservers-uk")
+            .name("Rackspace Cloud Servers UK")
+            .api(new CloudServersApiMetadata())
+            .homepage(URI.create("http://www.rackspace.co.uk/cloud-hosting/cloud-products/cloud-servers"))
+            .console(URI.create("https://lon.manage.rackspacecloud.com"))
+            .linkedServices("cloudloadbalancers-uk", "cloudservers-uk", "cloudfiles-uk")
+            .iso3166Codes("GB-SLG"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Rackspace Cloud Servers UK";
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudServersUKProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.rackspace.co.uk/cloud-hosting/cloud-products/cloud-servers");
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudServersUKProviderMetadata build() {
+         return new CloudServersUKProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://lon.manage.rackspacecloud.com");
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("cloudfiles-uk", "cloudservers-uk", "cloudloadbalancers-uk");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("GB-SLG");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
\ No newline at end of file
diff --git a/providers/cloudservers-uk/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersUKProviderTest.java b/providers/cloudservers-uk/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersUKProviderTest.java
index 602c82f..e0011cc 100644
--- a/providers/cloudservers-uk/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersUKProviderTest.java
+++ b/providers/cloudservers-uk/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersUKProviderTest.java
@@ -35,8 +35,8 @@
  */
 package org.jclouds.rackspace.cloudservers;
 
+import org.jclouds.cloudservers.CloudServersApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -47,6 +47,6 @@
 public class CloudServersUKProviderTest extends BaseProviderMetadataTest {
 
    public CloudServersUKProviderTest() {
-      super(new CloudServersUKProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new CloudServersUKProviderMetadata(), new CloudServersApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersUSProviderMetadata.java b/providers/cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersUSProviderMetadata.java
index 2ae4c62..698f96f 100644
--- a/providers/cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersUSProviderMetadata.java
+++ b/providers/cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/CloudServersUSProviderMetadata.java
@@ -19,11 +19,9 @@
 package org.jclouds.rackspace.cloudservers;
 
 import java.net.URI;
-import java.util.Set;
 
-import org.jclouds.cloudservers.CloudServersProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.cloudservers.CloudServersApiMetadata;
+import org.jclouds.providers.BaseProviderMetadata;
 
 
 /**
@@ -31,54 +29,39 @@
  * 
  * @author Adrian Cole
  */
-public class CloudServersUSProviderMetadata extends CloudServersProviderMetadata {
+public class CloudServersUSProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudservers-us";
+   public CloudServersUSProviderMetadata() {
+      this(builder()
+            .id("cloudservers-us")
+            .name("Rackspace Cloud Servers US")
+            .api(new CloudServersApiMetadata())
+            .homepage(URI.create("http://www.rackspace.com/cloud/cloud_hosting_products/servers"))
+            .console(URI.create("https://manage.rackspacecloud.com"))
+            .linkedServices("cloudloadbalancers-us", "cloudservers-us", "cloudfiles-us")
+            .iso3166Codes("US-IL", "US-TX"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Rackspace Cloud Servers US";
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudServersUSProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.rackspace.com/cloud/cloud_hosting_products/servers");
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudServersUSProviderMetadata build() {
+         return new CloudServersUSProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://manage.rackspacecloud.com");
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("cloudfiles-us", "cloudservers-us", "cloudloadbalancers-us");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-IL", "US-TX");
-   }
-
 }
\ No newline at end of file
diff --git a/providers/cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersUSProviderTest.java b/providers/cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersUSProviderTest.java
index 71da624..6f4ec86 100644
--- a/providers/cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersUSProviderTest.java
+++ b/providers/cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/CloudServersUSProviderTest.java
@@ -35,8 +35,8 @@
  */
 package org.jclouds.rackspace.cloudservers;
 
+import org.jclouds.cloudservers.CloudServersApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -47,6 +47,6 @@
 public class CloudServersUSProviderTest extends BaseProviderMetadataTest {
 
    public CloudServersUSProviderTest() {
-      super(new CloudServersUSProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new CloudServersUSProviderMetadata(), new CloudServersApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/cloudsigma-lvs/src/main/java/org/jclouds/cloudsigma/CloudSigmaLasVegasProviderMetadata.java b/providers/cloudsigma-lvs/src/main/java/org/jclouds/cloudsigma/CloudSigmaLasVegasProviderMetadata.java
index b2e7509..12970c0 100644
--- a/providers/cloudsigma-lvs/src/main/java/org/jclouds/cloudsigma/CloudSigmaLasVegasProviderMetadata.java
+++ b/providers/cloudsigma-lvs/src/main/java/org/jclouds/cloudsigma/CloudSigmaLasVegasProviderMetadata.java
@@ -19,97 +19,46 @@
 package org.jclouds.cloudsigma;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
- * Implementation of {@link org.jclouds.types.ProviderMetadata} for CloudSigma LasVegas.
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for CloudSigma Las Vegas.
  *
  * @author Adrian Cole
  */
 public class CloudSigmaLasVegasProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudsigma-lvs";
+   public CloudSigmaLasVegasProviderMetadata() {
+      this(builder()
+            .id("cloudsigma-lvs")
+            .name("CloudSigma Las Vegas")
+            .api(new CloudSigmaApiMetadata())
+            .homepage(URI.create("http://www.cloudsigma.com/en/our-cloud/features"))
+            .console(URI.create("https://gui.lvs.cloudsigma.com/"))
+            .iso3166Codes("US-NV"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudSigmaLasVegasProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "CloudSigma LasVegas";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudSigmaLasVegasProviderMetadata build() {
+         return new CloudSigmaLasVegasProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Email";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Password";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.cloudsigma.com/en/our-cloud/features");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://gui.lvs.cloudsigma.com/");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://cloudsigma.com/en/platform-details/the-api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("cloudsigma-lvs");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-LV");
-   }
-
 }
diff --git a/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/CloudSigmaLasVegasProviderTest.java b/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/CloudSigmaLasVegasProviderTest.java
index 55aea80..0afe6d8 100644
--- a/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/CloudSigmaLasVegasProviderTest.java
+++ b/providers/cloudsigma-lvs/src/test/java/org/jclouds/cloudsigma/CloudSigmaLasVegasProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.cloudsigma;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +29,6 @@
 public class CloudSigmaLasVegasProviderTest extends BaseProviderMetadataTest {
 
    public CloudSigmaLasVegasProviderTest() {
-      super(new CloudSigmaLasVegasProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new CloudSigmaLasVegasProviderMetadata(), new CloudSigmaApiMetadata());
    }
 }
diff --git a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/CloudSigmaZurichProviderMetadata.java b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/CloudSigmaZurichProviderMetadata.java
index f4ee3f1..483f22e 100644
--- a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/CloudSigmaZurichProviderMetadata.java
+++ b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/CloudSigmaZurichProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.cloudsigma;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for CloudSigma Zurich.
@@ -33,83 +29,36 @@
  */
 public class CloudSigmaZurichProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "cloudsigma-zrh";
+   public CloudSigmaZurichProviderMetadata() {
+      this(builder()
+            .id("cloudsigma-zrh")
+            .name("CloudSigma Zurich")
+            .api(new CloudSigmaApiMetadata())
+            .homepage(URI.create("http://www.cloudsigma.com/en/our-cloud/features"))
+            .console(URI.create("https://gui.zrh.cloudsigma.com/"))
+            .iso3166Codes("CH-ZH"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected CloudSigmaZurichProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "CloudSigma Zurich";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public CloudSigmaZurichProviderMetadata build() {
+         return new CloudSigmaZurichProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Email";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Password";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.cloudsigma.com/en/our-cloud/features");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://gui.zrh.cloudsigma.com/");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://cloudsigma.com/en/platform-details/the-api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("cloudsigma-zrh");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("CH-ZH");
-   }
-
 }
diff --git a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichProviderTest.java b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichProviderTest.java
index 7e6d13c..2e9d1d8 100644
--- a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichProviderTest.java
+++ b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaZurichProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.cloudsigma;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +29,6 @@
 public class CloudSigmaZurichProviderTest extends BaseProviderMetadataTest {
 
    public CloudSigmaZurichProviderTest() {
-      super(new CloudSigmaZurichProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new CloudSigmaZurichProviderMetadata(), new CloudSigmaApiMetadata());
    }
 }
diff --git a/providers/elastichosts-lax-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesProviderMetadata.java b/providers/elastichosts-lax-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesProviderMetadata.java
index 0f08edc..ec5cdd1 100644
--- a/providers/elastichosts-lax-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesProviderMetadata.java
+++ b/providers/elastichosts-lax-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesProviderMetadata.java
@@ -19,97 +19,47 @@
 package org.jclouds.elastichosts;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
- * Implementation of {@link org.jclouds.types.ProviderMetadata} for ElasticHosts Toronto Peer 1.
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for ElasticHosts Los Angeles Peer 1.
  *
  * @author Adrian Cole
  */
 public class ElasticHostsPeer1LosAngelesProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "elastichosts-lax-p";
+   public ElasticHostsPeer1LosAngelesProviderMetadata() {
+      this(builder()
+            .id("elastichosts-lax-p")
+            .name("ElasticHosts Los Angeles Peer 1")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("https://lax-p.elastichosts.com"))
+            .console(URI.create("https://lax-p.elastichosts.com/accounts"))
+            .iso3166Codes("US-CA"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected ElasticHostsPeer1LosAngelesProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "ElasticHosts Los Angeles Peer 1";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public ElasticHostsPeer1LosAngelesProviderMetadata build() {
+         return new ElasticHostsPeer1LosAngelesProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API key";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://lax-p.elastichosts.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://lax-p.elastichosts.com/accounts");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.elastichosts.com/cloud-hosting/api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("elastichosts-lax-p");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-CA");
-   }
-
 }
diff --git a/providers/elastichosts-lax-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesProviderTest.java b/providers/elastichosts-lax-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesProviderTest.java
index f5a34fc..bc8e763 100644
--- a/providers/elastichosts-lax-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesProviderTest.java
+++ b/providers/elastichosts-lax-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LosAngelesProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.elastichosts;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class ElasticHostsPeer1LosAngelesProviderTest extends BaseProviderMetadataTest {
 
    public ElasticHostsPeer1LosAngelesProviderTest() {
-      super(new ElasticHostsPeer1LosAngelesProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new ElasticHostsPeer1LosAngelesProviderMetadata(), new ElasticStackApiMetadata());
    }
 }
diff --git a/providers/elastichosts-lon-b/src/main/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonProviderMetadata.java b/providers/elastichosts-lon-b/src/main/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonProviderMetadata.java
index ca6c702..dcf1e4a 100644
--- a/providers/elastichosts-lon-b/src/main/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonProviderMetadata.java
+++ b/providers/elastichosts-lon-b/src/main/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.elastichosts;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for ElasticHosts London BlueSquare.
@@ -33,83 +30,37 @@
  */
 public class ElasticHostsBlueSquareLondonProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "elastichosts-lon-b";
+   public ElasticHostsBlueSquareLondonProviderMetadata() {
+      this(builder()
+            .id("elastichosts-lon-b")
+            .name("ElasticHosts Los Angeles BlueSquare")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("https://lon-b.elastichosts.com"))
+            .console(URI.create("https://lon-b.elastichosts.com/accounts"))
+            .iso3166Codes("GB-LND"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected ElasticHostsBlueSquareLondonProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "ElasticHosts London BlueSquare";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public ElasticHostsBlueSquareLondonProviderMetadata build() {
+         return new ElasticHostsBlueSquareLondonProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://lon-b.elastichosts.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://lon-b.elastichosts.com/accounts");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.elastichosts.com/cloud-hosting/api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("elastichosts-lon-b");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("GB-LND");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/elastichosts-lon-b/src/test/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonProviderTest.java b/providers/elastichosts-lon-b/src/test/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonProviderTest.java
index 2e7f7b4..b339d08 100644
--- a/providers/elastichosts-lon-b/src/test/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonProviderTest.java
+++ b/providers/elastichosts-lon-b/src/test/java/org/jclouds/elastichosts/ElasticHostsBlueSquareLondonProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.elastichosts;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class ElasticHostsBlueSquareLondonProviderTest extends BaseProviderMetadataTest {
 
    public ElasticHostsBlueSquareLondonProviderTest() {
-      super(new ElasticHostsBlueSquareLondonProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new ElasticHostsBlueSquareLondonProviderMetadata(), new ElasticStackApiMetadata());
    }
 }
diff --git a/providers/elastichosts-lon-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonProviderMetadata.java b/providers/elastichosts-lon-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonProviderMetadata.java
index c98bf03..c68185e 100644
--- a/providers/elastichosts-lon-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonProviderMetadata.java
+++ b/providers/elastichosts-lon-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.elastichosts;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for ElasticHosts London Peer 1.
@@ -33,83 +30,37 @@
  */
 public class ElasticHostsPeer1LondonProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "elastichosts-lon-p";
+   public ElasticHostsPeer1LondonProviderMetadata() {
+      this(builder()
+            .id("elastichosts-lon-p")
+            .name("ElasticHosts Los Angeles Peer 1")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("https://lon-p.elastichosts.com"))
+            .console(URI.create("https://lon-p.elastichosts.com/accounts"))
+            .iso3166Codes("GB-LND"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected ElasticHostsPeer1LondonProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "ElasticHosts London Peer 1";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public ElasticHostsPeer1LondonProviderMetadata build() {
+         return new ElasticHostsPeer1LondonProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://lon-p.elastichosts.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://lon-p.elastichosts.com/accounts");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.elastichosts.com/cloud-hosting/api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("elastichosts-lon-p");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("GB-LND");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/elastichosts-lon-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonProviderTest.java b/providers/elastichosts-lon-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonProviderTest.java
index 5ab9481..7763951 100644
--- a/providers/elastichosts-lon-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonProviderTest.java
+++ b/providers/elastichosts-lon-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1LondonProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.elastichosts;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class ElasticHostsPeer1LondonProviderTest extends BaseProviderMetadataTest {
 
    public ElasticHostsPeer1LondonProviderTest() {
-      super(new ElasticHostsPeer1LondonProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new ElasticHostsPeer1LondonProviderMetadata(), new ElasticStackApiMetadata());
    }
 }
diff --git a/providers/elastichosts-sat-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioProviderMetadata.java b/providers/elastichosts-sat-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioProviderMetadata.java
index 1f84bc3..3643598 100644
--- a/providers/elastichosts-sat-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioProviderMetadata.java
+++ b/providers/elastichosts-sat-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioProviderMetadata.java
@@ -1,6 +1,6 @@
 /**
  * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
+ * contribusat 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
@@ -19,12 +19,9 @@
 package org.jclouds.elastichosts;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for ElasticHosts San Antonio Peer 1.
@@ -33,83 +30,36 @@
  */
 public class ElasticHostsPeer1SanAntonioProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "elastichosts-sat-p";
+   public ElasticHostsPeer1SanAntonioProviderMetadata() {
+      this(builder()
+            .id("elastichosts-sat-p")
+            .name("ElasticHosts San Antonio Peer 1")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("https://sat-p.elastichosts.com"))
+            .console(URI.create("https://sat-p.elastichosts.com/accounts"))
+            .iso3166Codes("US-TX"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected ElasticHostsPeer1SanAntonioProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "ElasticHosts San Antonio Peer 1";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public ElasticHostsPeer1SanAntonioProviderMetadata build() {
+         return new ElasticHostsPeer1SanAntonioProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API key";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://sat-p.elastichosts.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://sat-p.elastichosts.com/accounts");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.elastichosts.com/cloud-hosting/api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("elastichosts-sat-p");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-TX");
-   }
-
 }
\ No newline at end of file
diff --git a/providers/elastichosts-sat-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioProviderTest.java b/providers/elastichosts-sat-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioProviderTest.java
index 25f94ea..9c6cae4 100644
--- a/providers/elastichosts-sat-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioProviderTest.java
+++ b/providers/elastichosts-sat-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1SanAntonioProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.elastichosts;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class ElasticHostsPeer1SanAntonioProviderTest extends BaseProviderMetadataTest {
 
    public ElasticHostsPeer1SanAntonioProviderTest() {
-      super(new ElasticHostsPeer1SanAntonioProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new ElasticHostsPeer1SanAntonioProviderMetadata(), new ElasticStackApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/elastichosts-tor-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoProviderMetadata.java b/providers/elastichosts-tor-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoProviderMetadata.java
index 9a0000f..921c42f 100644
--- a/providers/elastichosts-tor-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoProviderMetadata.java
+++ b/providers/elastichosts-tor-p/src/main/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.elastichosts;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for ElasticHosts Toronto Peer 1.
@@ -33,83 +30,36 @@
  */
 public class ElasticHostsPeer1TorontoProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "elastichosts-tor-p";
+   public ElasticHostsPeer1TorontoProviderMetadata() {
+      this(builder()
+            .id("elastichosts-tor-p")
+            .name("ElasticHosts Toronto Peer 1")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("https://tor-p.elastichosts.com"))
+            .console(URI.create("https://tor-p.elastichosts.com/accounts"))
+            .iso3166Codes("CA-ON"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected ElasticHostsPeer1TorontoProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "ElasticHosts Toronto Peer 1";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public ElasticHostsPeer1TorontoProviderMetadata build() {
+         return new ElasticHostsPeer1TorontoProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API key";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://tor-p.elastichosts.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://tor-p.elastichosts.com/accounts");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.elastichosts.com/cloud-hosting/api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("elastichosts-tor-p");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("CA-ON");
-   }
-
 }
\ No newline at end of file
diff --git a/providers/elastichosts-tor-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoProviderTest.java b/providers/elastichosts-tor-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoProviderTest.java
index 2254d87..9164406 100644
--- a/providers/elastichosts-tor-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoProviderTest.java
+++ b/providers/elastichosts-tor-p/src/test/java/org/jclouds/elastichosts/ElasticHostsPeer1TorontoProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.elastichosts;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class ElasticHostsPeer1TorontoProviderTest extends BaseProviderMetadataTest {
 
    public ElasticHostsPeer1TorontoProviderTest() {
-      super(new ElasticHostsPeer1TorontoProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new ElasticHostsPeer1TorontoProviderMetadata(), new ElasticStackApiMetadata());
    }
 }
\ No newline at end of file
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 f40b42d..ebac17f 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
@@ -19,12 +19,10 @@
 package org.jclouds.epc;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.eucalyptus.EucalyptusApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
 
-import com.google.common.collect.ImmutableSet;
-
 /**
  * Implementation of {@ link org.jclouds.types.ProviderMetadata} for Eucalpytus'
  * Partner Cloud EC2 provider.
@@ -33,84 +31,38 @@
  */
 public class EucalyptusPartnerCloudEC2ProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "eucalyptus-partnercloud-ec2";
+   public EucalyptusPartnerCloudEC2ProviderMetadata() {
+      this(builder()
+            .id("eucalyptus-partnercloud-ec2")
+            .name("Eucalyptus Partner Cloud (EC2)")
+            .api(new EucalyptusApiMetadata())
+            .homepage(URI.create("http://www.eucalyptus.com/partners"))
+            .console(URI.create("https://partnercloud.eucalyptus.com:8443"))
+            .linkedServices("eucalyptus-partnercloud-ec2", "eucalyptus-partnercloud-s3")
+            .iso3166Codes("US-CA"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected EucalyptusPartnerCloudEC2ProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Eucalyptus Partner Cloud (EC2)";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public EucalyptusPartnerCloudEC2ProviderMetadata build() {
+         return new EucalyptusPartnerCloudEC2ProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Username";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Password";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.eucalyptus.com/partners");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://partnercloud.eucalyptus.com:8443");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://open.eucalyptus.com/wiki/IntroducingEucalyptus_v2.0");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("eucalyptus-partnercloud-ec2", "eucalyptus-partnercloud-s3");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-CA");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/eucalyptus-partnercloud-ec2/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudEC2ProviderTest.java b/providers/eucalyptus-partnercloud-ec2/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudEC2ProviderTest.java
index c740e09..54292a8 100644
--- a/providers/eucalyptus-partnercloud-ec2/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudEC2ProviderTest.java
+++ b/providers/eucalyptus-partnercloud-ec2/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudEC2ProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.epc;
 
+import org.jclouds.eucalyptus.EucalyptusApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +31,7 @@
 public class EucalyptusPartnerCloudEC2ProviderTest extends BaseProviderMetadataTest {
 
    public EucalyptusPartnerCloudEC2ProviderTest() {
-      super(new EucalyptusPartnerCloudEC2ProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new EucalyptusPartnerCloudEC2ProviderMetadata(), new EucalyptusApiMetadata());
    }
 
 }
\ No newline at end of file
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 ba0b1c7..9afe679 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
@@ -19,11 +19,9 @@
 package org.jclouds.epc;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.walrus.WalrusApiMetadata;
 
 /**
  * Implementation of {@ link org.jclouds.types.ProviderMetadata} for Eucalpytus'
@@ -33,84 +31,38 @@
  */
 public class EucalyptusPartnerCloudS3ProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "eucalyptus-partnercloud-s3";
+   public EucalyptusPartnerCloudS3ProviderMetadata() {
+      this(builder()
+            .id("eucalyptus-partnercloud-s3")
+            .name("Eucalyptus Partner Cloud (S3)")
+            .api(new WalrusApiMetadata())
+            .homepage(URI.create("http://www.eucalyptus.com/partners"))
+            .console(URI.create("https://partnercloud.eucalyptus.com:8443"))
+            .linkedServices("eucalyptus-partnercloud-s3", "eucalyptus-partnercloud-s3")
+            .iso3166Codes("US-CA"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return BLOBSTORE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected EucalyptusPartnerCloudS3ProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Eucalyptus Partner Cloud (S3)";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public EucalyptusPartnerCloudS3ProviderMetadata build() {
+         return new EucalyptusPartnerCloudS3ProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Username";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Password";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.eucalyptus.com/partners");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://partnercloud.eucalyptus.com:8443");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://open.eucalyptus.com/wiki/IntroducingEucalyptus_v2.0");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("eucalyptus-partnercloud-ec2", "eucalyptus-partnercloud-s3");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-CA");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/eucalyptus-partnercloud-s3/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudS3ProviderTest.java b/providers/eucalyptus-partnercloud-s3/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudS3ProviderTest.java
index ec22eaa..cfdabb7 100644
--- a/providers/eucalyptus-partnercloud-s3/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudS3ProviderTest.java
+++ b/providers/eucalyptus-partnercloud-s3/src/test/java/org/jclouds/epc/EucalyptusPartnerCloudS3ProviderTest.java
@@ -19,7 +19,7 @@
 package org.jclouds.epc;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.walrus.WalrusApiMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +31,7 @@
 public class EucalyptusPartnerCloudS3ProviderTest extends BaseProviderMetadataTest {
 
    public EucalyptusPartnerCloudS3ProviderTest() {
-      super(new EucalyptusPartnerCloudS3ProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new EucalyptusPartnerCloudS3ProviderMetadata(), new WalrusApiMetadata());
    }
 
 }
\ No newline at end of file
diff --git a/providers/go2cloud-jhb1/src/main/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ProviderMetadata.java b/providers/go2cloud-jhb1/src/main/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ProviderMetadata.java
index 4afca17..90e28f6 100644
--- a/providers/go2cloud-jhb1/src/main/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ProviderMetadata.java
+++ b/providers/go2cloud-jhb1/src/main/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ProviderMetadata.java
@@ -19,12 +19,10 @@
 package org.jclouds.go2cloud;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
 
-import com.google.common.collect.ImmutableSet;
-
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Go2Cloud's
  * Johannesburg1 provider.
@@ -33,76 +31,37 @@
  */
 public class Go2CloudJohannesburg1ProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "go2cloud-jhb1";
+   public Go2CloudJohannesburg1ProviderMetadata() {
+      this(builder()
+            .id("go2cloud-jhb1")
+            .name("Go2Cloud Johannesburg1")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("https://jhb1.go2cloud.co.za"))
+            .console(URI.create("https://jhb1.go2cloud.co.za/accounts"))
+            .iso3166Codes("ZA-GP"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected Go2CloudJohannesburg1ProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Go2Cloud Johannesburg1";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public Go2CloudJohannesburg1ProviderMetadata build() {
+         return new Go2CloudJohannesburg1ProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "User UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://jhb1.go2cloud.co.za/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("http://jhb1.go2cloud.co.za/accounts");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.go2cloud.co.za/Home/QuickStart");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("ZA-GP");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/go2cloud-jhb1/src/test/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ProviderTest.java b/providers/go2cloud-jhb1/src/test/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ProviderTest.java
index 7d5959f..22a49cb 100644
--- a/providers/go2cloud-jhb1/src/test/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ProviderTest.java
+++ b/providers/go2cloud-jhb1/src/test/java/org/jclouds/go2cloud/Go2CloudJohannesburg1ProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.go2cloud;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +31,7 @@
 public class Go2CloudJohannesburg1ProviderTest extends BaseProviderMetadataTest {
 
    public Go2CloudJohannesburg1ProviderTest() {
-      super(new Go2CloudJohannesburg1ProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new Go2CloudJohannesburg1ProviderMetadata(), new ElasticStackApiMetadata());
    }
 
 }
diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridApiMetadata.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridApiMetadata.java
new file mode 100644
index 0000000..e209e90
--- /dev/null
+++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.gogrid;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the GoGrid API
+ * 
+ * @author Adrian Cole
+ */
+public class GoGridApiMetadata extends BaseApiMetadata {
+
+   public GoGridApiMetadata() {
+      this(builder()
+            .id("gogrid")
+            .type(ApiType.COMPUTE)
+            .name("GoGrid API")
+            .identityName("API Key")
+            .credentialName("Shared Secret")
+            .documentation(URI.create("https://wiki.gogrid.com/wiki/index.php/API")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected GoGridApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public GoGridApiMetadata build() {
+         return new GoGridApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridProviderMetadata.java b/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridProviderMetadata.java
index 1d74d7c..8221241 100644
--- a/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridProviderMetadata.java
+++ b/providers/gogrid/src/main/java/org/jclouds/gogrid/GoGridProviderMetadata.java
@@ -19,98 +19,46 @@
 package org.jclouds.gogrid;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
- * Implementation of @ link org.jclouds.types.ProviderMetadata} for GoGrid.
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for GoGrid.
  * 
  * @author Adrian Cole
  */
 public class GoGridProviderMetadata extends BaseProviderMetadata {
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "gogrid";
+   public GoGridProviderMetadata() {
+      this(builder()
+            .id("gogrid")
+            .name("GoGrid")
+            .api(new GoGridApiMetadata())
+            .homepage(URI.create("http://www.gogrid.com"))
+            .console(URI.create("https://my.gogrid.com/gogrid"))
+            .iso3166Codes("US-CA", "US-VA", "BR-SP"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected GoGridProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "GoGrid";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public GoGridProviderMetadata build() {
+         return new GoGridProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "API Key";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Shared Secret";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.gogrid.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://my.gogrid.com/gogrid");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("https://wiki.gogrid.com/wiki/index.php/API");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("gogrid");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-CA", "US-VA", "BR-SP");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
\ No newline at end of file
diff --git a/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridProviderTest.java b/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridProviderTest.java
index 120e8ac..2fb9140 100644
--- a/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridProviderTest.java
+++ b/providers/gogrid/src/test/java/org/jclouds/gogrid/GoGridProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.gogrid;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +29,6 @@
 public class GoGridProviderTest extends BaseProviderMetadataTest {
 
    public GoGridProviderTest() {
-      super(new GoGridProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new GoGridProviderMetadata(), new GoGridApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/greenhousedata-element-vcloud/src/main/java/org/jclouds/greenhousedata/element/vcloud/GreenHouseDataElementVCloudProviderMetadata.java b/providers/greenhousedata-element-vcloud/src/main/java/org/jclouds/greenhousedata/element/vcloud/GreenHouseDataElementVCloudProviderMetadata.java
index 6c088f0..a06f740 100644
--- a/providers/greenhousedata-element-vcloud/src/main/java/org/jclouds/greenhousedata/element/vcloud/GreenHouseDataElementVCloudProviderMetadata.java
+++ b/providers/greenhousedata-element-vcloud/src/main/java/org/jclouds/greenhousedata/element/vcloud/GreenHouseDataElementVCloudProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.greenhousedata.element.vcloud;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.vcloud.VCloudApiMetadata;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Green House Data Element vCloud
@@ -33,84 +30,37 @@
  */
 public class GreenHouseDataElementVCloudProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "greenhousedata-element-vcloud";
+   public GreenHouseDataElementVCloudProviderMetadata() {
+      this(builder()
+            .id("greenhousedata-element-vcloud")
+            .name("Green House Data Element vCloud")
+            .api(new VCloudApiMetadata())
+            .homepage(URI.create("http://www.greenhousedata.com/element-cloud-hosting/vcloud-services/"))
+            .console(URI.create("https://mycloud.greenhousedata.com/cloud/org/YOUR_ORG_HERE"))
+            .iso3166Codes("US-WY"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected GreenHouseDataElementVCloudProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Green House Data Element vCloud";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public GreenHouseDataElementVCloudProviderMetadata build() {
+         return new GreenHouseDataElementVCloudProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "User at Organization (user@org)";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Password";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.greenhousedata.com/element-cloud-hosting/vcloud-services/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://mycloud.greenhousedata.com/cloud/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.greenhousedata.com/element-cloud-hosting/vcloud-services/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("greenhousedata-element-vcloud");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-WY");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/greenhousedata-element-vcloud/src/test/java/org/jclouds/greenhousedata/element/vcloud/GreenHouseDataElementVCloudProviderTest.java b/providers/greenhousedata-element-vcloud/src/test/java/org/jclouds/greenhousedata/element/vcloud/GreenHouseDataElementVCloudProviderTest.java
index 02f784a..b72914a 100644
--- a/providers/greenhousedata-element-vcloud/src/test/java/org/jclouds/greenhousedata/element/vcloud/GreenHouseDataElementVCloudProviderTest.java
+++ b/providers/greenhousedata-element-vcloud/src/test/java/org/jclouds/greenhousedata/element/vcloud/GreenHouseDataElementVCloudProviderTest.java
@@ -19,7 +19,7 @@
 package org.jclouds.greenhousedata.element.vcloud;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.vcloud.VCloudApiMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class GreenHouseDataElementVCloudProviderTest extends BaseProviderMetadataTest {
 
    public GreenHouseDataElementVCloudProviderTest() {
-      super(new GreenHouseDataElementVCloudProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new GreenHouseDataElementVCloudProviderMetadata(), new VCloudApiMetadata());
    }
 }
diff --git a/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeApiMetadata.java b/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeApiMetadata.java
new file mode 100644
index 0000000..05596e2
--- /dev/null
+++ b/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeApiMetadata.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.hpcloud.compute;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.openstack.nova.v1_1.NovaApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the Amazon-specific Nova API
+ * 
+ * @author Adrian Cole
+ */
+public class HPCloudComputeApiMetadata extends NovaApiMetadata {
+
+   public HPCloudComputeApiMetadata() {
+      this(builder().fromApiMetadata(new NovaApiMetadata())
+            .identityName("tenantId:accessKey")
+            .credentialName("secretKey"));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected HPCloudComputeApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends NovaApiMetadataBuilder<ConcreteBuilder> {
+
+      @Override
+      public HPCloudComputeApiMetadata build() {
+         return new HPCloudComputeApiMetadata(this);
+      }
+   }
+
+   private static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java b/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java
index 4084cca..5287499 100644
--- a/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java
+++ b/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.hpcloud.compute;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 
 /**
@@ -34,84 +30,37 @@
  */
 public class HPCloudComputeProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "hpcloud-compute";
+   public HPCloudComputeProviderMetadata() {
+      this(builder()
+            .id("hpcloud-compute")
+            .name("HP Cloud Compute Services")
+            .api(new HPCloudComputeApiMetadata())
+            .homepage(URI.create("http://hpcloud.com"))
+            .console(URI.create("https://manage.hpcloud.com/compute"))
+            .linkedServices("hpcloud-compute", "hpcloud-objectstorage")
+            .iso3166Codes("US-NV"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected HPCloudComputeProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "tenantId:accessKey";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public HPCloudComputeProviderMetadata build() {
+         return new HPCloudComputeProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret Key";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "HP Cloud Compute Services";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://hpcloud.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://manage.hpcloud.com/compute");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("TODO");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("hpcloud-compute", "hpcloud-objectstorage");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-NV");
-   }
-
 }
diff --git a/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderTest.java b/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderTest.java
index 783b8d6..670349b 100644
--- a/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderTest.java
+++ b/providers/hpcloud-compute/src/test/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderTest.java
@@ -36,7 +36,6 @@
 package org.jclouds.hpcloud.compute;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -47,6 +46,6 @@
 public class HPCloudComputeProviderTest extends BaseProviderMetadataTest {
 
    public HPCloudComputeProviderTest() {
-      super(new HPCloudComputeProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new HPCloudComputeProviderMetadata(), new HPCloudComputeApiMetadata());
    }
 }
diff --git a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java
new file mode 100644
index 0000000..2278c43
--- /dev/null
+++ b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java
@@ -0,0 +1,49 @@
+package org.jclouds.hpcloud.objectstorage;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for HP Cloud Object Storage API
+ * 
+ * @author Adrian Cole
+ */
+public class HPCloudObjectStorageApiMetadata extends BaseApiMetadata {
+
+   public HPCloudObjectStorageApiMetadata() {
+      this(builder()
+            .id("hpcloud-objectstorage")
+            .type(ApiType.BLOBSTORE)
+            .name("HP Cloud Services Object Storage API")
+            .identityName("tenantId:accessKey")
+            .credentialName("secretKey")
+            .documentation(URI.create("https://build.hpcloud.com/object-storage/api")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected HPCloudObjectStorageApiMetadata(ConcreteBuilder builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public HPCloudObjectStorageApiMetadata build() {
+         return new HPCloudObjectStorageApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+}
\ No newline at end of file
diff --git a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderMetadata.java b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderMetadata.java
index 6693af3..a41c592 100644
--- a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderMetadata.java
+++ b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.hpcloud.objectstorage;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.providers.ProviderMetadata} for HP Cloud Services Object Storage
@@ -33,84 +29,38 @@
  */
 public class HPCloudObjectStorageProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "hpcloud-objectstorage";
+   public HPCloudObjectStorageProviderMetadata() {
+      this(builder()
+            .id("hpcloud-objectstorage")
+            .name("HP Cloud Services Object Storage")
+            .api(new HPCloudObjectStorageApiMetadata())
+            .homepage(URI.create("http://hpcloud.com"))
+            .console(URI.create("https://manage.hpcloud.com/objects/us-west"))
+            .linkedServices("hpcloud-compute", "hpcloud-objectstorage")
+            .iso3166Codes("US-NV"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.BLOBSTORE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected HPCloudObjectStorageProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "HP Cloud Services Object Storage";
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "email:tenantId";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public HPCloudObjectStorageProviderMetadata build() {
+         return new HPCloudObjectStorageProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "API Key";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://hpcloud.com");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://manage.hpcloud.com/objects/us-west");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("TODO");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("hpcloud-compute", "hpcloud-objectstorage");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-NV");
-   }
-
-}
+}
\ No newline at end of file
diff --git a/providers/hpcloud-objectstorage/src/test/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderTest.java b/providers/hpcloud-objectstorage/src/test/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderTest.java
index b552bd0..2ec0636 100644
--- a/providers/hpcloud-objectstorage/src/test/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderTest.java
+++ b/providers/hpcloud-objectstorage/src/test/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.hpcloud.objectstorage;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +30,7 @@
 public class HPCloudObjectStorageProviderTest extends BaseProviderMetadataTest {
 
    public HPCloudObjectStorageProviderTest() {
-      super(new HPCloudObjectStorageProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new HPCloudObjectStorageProviderMetadata(), new HPCloudObjectStorageApiMetadata());
    }
 
 }
diff --git a/providers/ninefold-compute/src/main/java/org/jclouds/ninefold/compute/NinefoldComputeProviderMetadata.java b/providers/ninefold-compute/src/main/java/org/jclouds/ninefold/compute/NinefoldComputeProviderMetadata.java
index c7cbc4ec..06b8a77 100644
--- a/providers/ninefold-compute/src/main/java/org/jclouds/ninefold/compute/NinefoldComputeProviderMetadata.java
+++ b/providers/ninefold-compute/src/main/java/org/jclouds/ninefold/compute/NinefoldComputeProviderMetadata.java
@@ -19,12 +19,10 @@
 package org.jclouds.ninefold.compute;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.cloudstack.CloudStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
 
-import com.google.common.collect.ImmutableSet;
-
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Ninefold
  * Compute.
@@ -33,83 +31,36 @@
  */
 public class NinefoldComputeProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "ninefold-compute";
+   public NinefoldComputeProviderMetadata() {
+      this(builder()
+            .id("ninefold-compute")
+            .name("Ninefold Compute")
+            .api(new CloudStackApiMetadata())
+            .homepage(URI.create("http://ninefold.com/virtual-servers/"))
+            .console(URI.create("https://ninefold.com/portal/portal/login"))
+            .iso3166Codes("AU-NSW"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected NinefoldComputeProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Ninefold Compute";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public NinefoldComputeProviderMetadata build() {
+         return new NinefoldComputeProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "API Key";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://ninefold.com/virtual-servers/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://ninefold.com/portal/portal/login");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("https://ninefold.com/support/display/SPT/Cloud+Compute");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("AU-NSW");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of(getId(), "ninefold-storage");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 }
diff --git a/providers/ninefold-compute/src/test/java/org/jclouds/ninefold/compute/NinefoldComputeProviderTest.java b/providers/ninefold-compute/src/test/java/org/jclouds/ninefold/compute/NinefoldComputeProviderTest.java
index a7bd1c6..0aec023 100644
--- a/providers/ninefold-compute/src/test/java/org/jclouds/ninefold/compute/NinefoldComputeProviderTest.java
+++ b/providers/ninefold-compute/src/test/java/org/jclouds/ninefold/compute/NinefoldComputeProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.ninefold.compute;
 
+import org.jclouds.cloudstack.CloudStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +31,7 @@
 public class NinefoldComputeProviderTest extends BaseProviderMetadataTest {
 
    public NinefoldComputeProviderTest() {
-      super(new NinefoldComputeProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new NinefoldComputeProviderMetadata(), new CloudStackApiMetadata());
    }
 
 }
diff --git a/providers/ninefold-storage/src/main/java/org/jclouds/ninefold/storage/NinefoldStorageProviderMetadata.java b/providers/ninefold-storage/src/main/java/org/jclouds/ninefold/storage/NinefoldStorageProviderMetadata.java
index 5658f5b..14e40a6 100644
--- a/providers/ninefold-storage/src/main/java/org/jclouds/ninefold/storage/NinefoldStorageProviderMetadata.java
+++ b/providers/ninefold-storage/src/main/java/org/jclouds/ninefold/storage/NinefoldStorageProviderMetadata.java
@@ -19,12 +19,10 @@
 package org.jclouds.ninefold.storage;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.atmos.AtmosApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
 
-import com.google.common.collect.ImmutableSet;
-
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Ninefold's
  * Storage provider.
@@ -33,76 +31,36 @@
  */
 public class NinefoldStorageProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "ninefold-storage";
+   public NinefoldStorageProviderMetadata() {
+      this(builder()
+            .id("ninefold-storage")
+            .name("Ninefold Storage")
+            .api(new AtmosApiMetadata())
+            .homepage(URI.create("http://ninefold.com/cloud-storage/"))
+            .console(URI.create("https://ninefold.com/portal/"))
+            .iso3166Codes("AU-NSW"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return BLOBSTORE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected NinefoldStorageProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Ninefold Storage";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public NinefoldStorageProviderMetadata build() {
+         return new NinefoldStorageProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Subtenant ID (UID)";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Shared Secret";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://ninefold.com/cloud-storage/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://ninefold.com/portal/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("https://ninefold.com/support/display/SPT/API+Documentation");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("AU-NSW");
-   }
-
 }
diff --git a/providers/ninefold-storage/src/test/java/org/jclouds/ninefold/storage/NinefoldStorageProviderTest.java b/providers/ninefold-storage/src/test/java/org/jclouds/ninefold/storage/NinefoldStorageProviderTest.java
index 2e8164c..499648c 100644
--- a/providers/ninefold-storage/src/test/java/org/jclouds/ninefold/storage/NinefoldStorageProviderTest.java
+++ b/providers/ninefold-storage/src/test/java/org/jclouds/ninefold/storage/NinefoldStorageProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.ninefold.storage;
 
+import org.jclouds.atmos.AtmosApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +31,7 @@
 public class NinefoldStorageProviderTest extends BaseProviderMetadataTest {
 
    public NinefoldStorageProviderTest() {
-      super(new NinefoldStorageProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new NinefoldStorageProviderMetadata(), new AtmosApiMetadata());
    }
 
 }
\ No newline at end of file
diff --git a/providers/openhosting-east1/src/main/java/org/jclouds/openhosting/OpenHostingEast1ProviderMetadata.java b/providers/openhosting-east1/src/main/java/org/jclouds/openhosting/OpenHostingEast1ProviderMetadata.java
index fabc13c..6944d07 100644
--- a/providers/openhosting-east1/src/main/java/org/jclouds/openhosting/OpenHostingEast1ProviderMetadata.java
+++ b/providers/openhosting-east1/src/main/java/org/jclouds/openhosting/OpenHostingEast1ProviderMetadata.java
@@ -19,90 +19,48 @@
 package org.jclouds.openhosting;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
 
-import com.google.common.collect.ImmutableSet;
-
 /**
- * Implementation of {@link org.jclouds.types.ProviderMetadata} for OpenHosting's
- * East1 provider.
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for ElasticHosts London Peer 1.
  *
- * @author Jeremy Whitlock <jwhitlock@apache.org>
+ * @author Adrian Cole
  */
 public class OpenHostingEast1ProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "openhosting-east1";
+   public OpenHostingEast1ProviderMetadata() {
+      this(builder()
+            .id("openhosting-east1")
+            .name("OpenHosting East1")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("https://east1.openhosting.com"))
+            .console(URI.create("https://east1.openhosting.com/accounts"))
+            .iso3166Codes("US-VA"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected OpenHostingEast1ProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "OpenHosting East1";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public OpenHostingEast1ProviderMetadata build() {
+         return new OpenHostingEast1ProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "User UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://east1.openhosting.com/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://east1.openhosting.com/accounts/login");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.openhosting.com/support/api/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-FL");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/OpenHostingEast1ProviderTest.java b/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/OpenHostingEast1ProviderTest.java
index 15cbca4e..7caa1d9 100644
--- a/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/OpenHostingEast1ProviderTest.java
+++ b/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/OpenHostingEast1ProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.openhosting;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +31,7 @@
 public class OpenHostingEast1ProviderTest extends BaseProviderMetadataTest {
 
    public OpenHostingEast1ProviderTest() {
-      super(new OpenHostingEast1ProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new OpenHostingEast1ProviderMetadata(), new ElasticStackApiMetadata());
    }
 
 }
\ No newline at end of file
diff --git a/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/compute/OpenHostingEast1TemplateBuilderLiveTest.java b/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/compute/OpenHostingEast1TemplateBuilderLiveTest.java
index c403ca4..96e00c3 100644
--- a/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/compute/OpenHostingEast1TemplateBuilderLiveTest.java
+++ b/providers/openhosting-east1/src/test/java/org/jclouds/openhosting/compute/OpenHostingEast1TemplateBuilderLiveTest.java
@@ -80,6 +80,6 @@
 
    @Override
    protected Set<String> getIso3166Codes() {
-      return ImmutableSet.<String> of("US-FL");
+      return ImmutableSet.<String> of("US-VA");
    }
 }
diff --git a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingApiMetadata.java b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingApiMetadata.java
new file mode 100644
index 0000000..795afc5
--- /dev/null
+++ b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingApiMetadata.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.rimuhosting.miro;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for RimuHosting API
+ * 
+ * @author Adrian Cole
+ */
+public class RimuHostingApiMetadata extends BaseApiMetadata {
+
+   public RimuHostingApiMetadata() {
+      this(builder()
+            .id("rimuhosting")
+            .type(ApiType.COMPUTE)
+            .name("RimuHosting API")
+            .identityName("API Key")
+            .documentation(URI.create("http://apidocs.rimuhosting.com")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected RimuHostingApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public RimuHostingApiMetadata build() {
+         return new RimuHostingApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingProviderMetadata.java b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingProviderMetadata.java
index 7c62d4c..b037c50 100644
--- a/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingProviderMetadata.java
+++ b/providers/rimuhosting/src/main/java/org/jclouds/rimuhosting/miro/RimuHostingProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.rimuhosting.miro;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for RimuHosting.
@@ -32,84 +28,37 @@
  * @author Adrian Cole
  */
 public class RimuHostingProviderMetadata extends BaseProviderMetadata {
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "rimuhosting";
+   public RimuHostingProviderMetadata() {
+      this(builder()
+            .id("rimuhosting")
+            .name("RimuHosting")
+            .api(new RimuHostingApiMetadata())
+            .homepage(URI.create("http://www.rimuhosting.com"))
+            .console(URI.create("https://rimuhosting.com/cp"))
+            .iso3166Codes("NZ-AUK", "US-TX", "AU-NSW", "GB-LND"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected RimuHostingProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "RimuHosting";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public RimuHostingProviderMetadata build() {
+         return new RimuHostingProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://rimuhosting.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://rimuhosting.com/cp");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://apidocs.rimuhosting.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("rimuhosting");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("NZ-AUK", "US-TX", "AU-NSW", "GB-LND");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/ProvidersInPropertiesTest.java b/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/ProvidersInPropertiesTest.java
deleted file mode 100644
index 701f725..0000000
--- a/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,47 +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.rimuhosting.miro;
-
-import org.jclouds.compute.util.ComputeServiceUtils;
-import org.jclouds.rest.Providers;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "rimuhosting") : providers;
-   }
-
-   @Test
-   public void testSupportedComputeServiceProviders() {
-      Iterable<String> providers = ComputeServiceUtils.getSupportedProviders();
-      assert Iterables.contains(providers, "rimuhosting") : providers;
-   }
-
-}
diff --git a/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingProviderTest.java b/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingProviderTest.java
index c85de52..de1ad2d 100644
--- a/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingProviderTest.java
+++ b/providers/rimuhosting/src/test/java/org/jclouds/rimuhosting/miro/RimuHostingProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.rimuhosting.miro;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +29,6 @@
 public class RimuHostingProviderTest extends BaseProviderMetadataTest {
 
    public RimuHostingProviderTest() {
-      super(new RimuHostingProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new RimuHostingProviderMetadata(), new RimuHostingApiMetadata());
    }
 }
diff --git a/providers/serverlove-z1-man/src/main/java/org/jclouds/serverlove/ServerloveManchesterProviderMetadata.java b/providers/serverlove-z1-man/src/main/java/org/jclouds/serverlove/ServerloveManchesterProviderMetadata.java
index d92ef04..d1b2230 100644
--- a/providers/serverlove-z1-man/src/main/java/org/jclouds/serverlove/ServerloveManchesterProviderMetadata.java
+++ b/providers/serverlove-z1-man/src/main/java/org/jclouds/serverlove/ServerloveManchesterProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.serverlove;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Serverlove Manchester.
@@ -33,83 +30,36 @@
  */
 public class ServerloveManchesterProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "serverlove-z1-man";
+   public ServerloveManchesterProviderMetadata() {
+      this(builder()
+            .id("serverlove-z1-man")
+            .name("Serverlove Manchester")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("http://www.serverlove.com"))
+            .console(URI.create("http://www.serverlove.com/accounts"))
+            .iso3166Codes("GB-MAN"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected ServerloveManchesterProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Serverlove Manchester";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public ServerloveManchesterProviderMetadata build() {
+         return new ServerloveManchesterProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API Key";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.serverlove.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("http://www.serverlove.com/login");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.serverlove.com/cloud-server-faqs/api-questions");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("serverlove-z1-man");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("GB-MAN");
-   }
-
 }
diff --git a/providers/serverlove-z1-man/src/test/java/org/jclouds/serverlove/ServerloveManchesterProviderTest.java b/providers/serverlove-z1-man/src/test/java/org/jclouds/serverlove/ServerloveManchesterProviderTest.java
index 6a6bd4a..9ad8d82 100644
--- a/providers/serverlove-z1-man/src/test/java/org/jclouds/serverlove/ServerloveManchesterProviderTest.java
+++ b/providers/serverlove-z1-man/src/test/java/org/jclouds/serverlove/ServerloveManchesterProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.serverlove;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class ServerloveManchesterProviderTest extends BaseProviderMetadataTest {
 
    public ServerloveManchesterProviderTest() {
-      super(new ServerloveManchesterProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new ServerloveManchesterProviderMetadata(), new ElasticStackApiMetadata());
    }
 }
diff --git a/providers/skalicloud-sdg-my/src/main/java/org/jclouds/skalicloud/SkaliCloudMalaysiaProviderMetadata.java b/providers/skalicloud-sdg-my/src/main/java/org/jclouds/skalicloud/SkaliCloudMalaysiaProviderMetadata.java
index 34563dc..d6a80aa 100644
--- a/providers/skalicloud-sdg-my/src/main/java/org/jclouds/skalicloud/SkaliCloudMalaysiaProviderMetadata.java
+++ b/providers/skalicloud-sdg-my/src/main/java/org/jclouds/skalicloud/SkaliCloudMalaysiaProviderMetadata.java
@@ -19,97 +19,48 @@
 package org.jclouds.skalicloud;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for SkaliCloud Malaysia.
  *
  * @author Adrian Cole
  */
-public class SkaliCloudMalaysiaProviderMetadata extends BaseProviderMetadata {
+public class SkaliCloudMalaysiaProviderMetadata  extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "skalicloud-sdg-my";
+   public SkaliCloudMalaysiaProviderMetadata() {
+      this(builder()
+            .id("skalicloud-sdg-my")
+            .name("SkaliCloud Malaysia")
+            .api(new ElasticStackApiMetadata())
+            .homepage(URI.create("http://sdg-my.skalicloud.com"))
+            .console(URI.create("http://sdg-my.skalicloud.com/accounts"))
+            .iso3166Codes("MY-10"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SkaliCloudMalaysiaProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "SkaliCloud Malaysia";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SkaliCloudMalaysiaProviderMetadata build() {
+         return new SkaliCloudMalaysiaProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "UUID";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Secret API Key";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.skalicloud.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("http://sdg-my.skalicloud.com");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.skalicloud.com/cloud-api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("skalicloud-sdg-my");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("MY-10");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/skalicloud-sdg-my/src/test/java/org/jclouds/skalicloud/SkaliCloudMalaysiaProviderTest.java b/providers/skalicloud-sdg-my/src/test/java/org/jclouds/skalicloud/SkaliCloudMalaysiaProviderTest.java
index 1ed3761..a45034b 100644
--- a/providers/skalicloud-sdg-my/src/test/java/org/jclouds/skalicloud/SkaliCloudMalaysiaProviderTest.java
+++ b/providers/skalicloud-sdg-my/src/test/java/org/jclouds/skalicloud/SkaliCloudMalaysiaProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.skalicloud;
 
+import org.jclouds.elasticstack.ElasticStackApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class SkaliCloudMalaysiaProviderTest extends BaseProviderMetadataTest {
 
    public SkaliCloudMalaysiaProviderTest() {
-      super(new SkaliCloudMalaysiaProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new SkaliCloudMalaysiaProviderMetadata(), new ElasticStackApiMetadata());
    }
 }
diff --git a/providers/slicehost/src/main/java/org/jclouds/slicehost/SlicehostApiMetadata.java b/providers/slicehost/src/main/java/org/jclouds/slicehost/SlicehostApiMetadata.java
new file mode 100644
index 0000000..7193e51
--- /dev/null
+++ b/providers/slicehost/src/main/java/org/jclouds/slicehost/SlicehostApiMetadata.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.slicehost;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Slicehost API
+ * 
+ * @author Adrian Cole
+ */
+public class SlicehostApiMetadata extends BaseApiMetadata {
+
+   public SlicehostApiMetadata() {
+      this(builder()
+            .id("slicehost")
+            .type(ApiType.COMPUTE)
+            .name("Slicehost API")
+            .identityName("API password")
+            .documentation(URI.create("http://articles.slicehost.com/api")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SlicehostApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SlicehostApiMetadata build() {
+         return new SlicehostApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/providers/slicehost/src/main/java/org/jclouds/slicehost/SlicehostProviderMetadata.java b/providers/slicehost/src/main/java/org/jclouds/slicehost/SlicehostProviderMetadata.java
index 34eb381..e315e8a 100644
--- a/providers/slicehost/src/main/java/org/jclouds/slicehost/SlicehostProviderMetadata.java
+++ b/providers/slicehost/src/main/java/org/jclouds/slicehost/SlicehostProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.slicehost;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Slicehost
@@ -32,85 +28,36 @@
  * @author Adrian Cole
  */
 public class SlicehostProviderMetadata extends BaseProviderMetadata {
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "slicehost";
+   public SlicehostProviderMetadata() {
+      this(builder()
+            .id("slicehost")
+            .name("Slicehost")
+            .api(new SlicehostApiMetadata())
+            .homepage(URI.create("http://www.slicehost.com"))
+            .console(URI.create("https://manage.slicehost.com/"))
+            .iso3166Codes("US-IL", "US-TX", "US-MO"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SlicehostProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Slicehost";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SlicehostProviderMetadata build() {
+         return new SlicehostProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "API password";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return null;
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.slicehost.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://manage.slicehost.com/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://articles.slicehost.com/api");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("slicehost");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-IL", "US-TX", "US-MO");
-   }
-
 }
\ No newline at end of file
diff --git a/providers/slicehost/src/test/java/org/jclouds/slicehost/SlicehostProviderTest.java b/providers/slicehost/src/test/java/org/jclouds/slicehost/SlicehostProviderTest.java
index 66ace27..44fe33b 100644
--- a/providers/slicehost/src/test/java/org/jclouds/slicehost/SlicehostProviderTest.java
+++ b/providers/slicehost/src/test/java/org/jclouds/slicehost/SlicehostProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.slicehost;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +29,6 @@
 public class SlicehostProviderTest extends BaseProviderMetadataTest {
 
    public SlicehostProviderTest() {
-      super(new SlicehostProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new SlicehostProviderMetadata(), new SlicehostApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerApiMetadata.java b/providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerApiMetadata.java
new file mode 100644
index 0000000..9650629
--- /dev/null
+++ b/providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerApiMetadata.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.softlayer;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for SoftLayer API
+ * 
+ * @author Adrian Cole
+ */
+public class SoftLayerApiMetadata extends BaseApiMetadata {
+
+   public SoftLayerApiMetadata() {
+      this(builder()
+            .id("softlayer")
+            .type(ApiType.COMPUTE)
+            .name("SoftLayer API")
+            .identityName("API Username")
+            .credentialName("API Key")
+            .documentation(URI.create("http://sldn.softlayer.com/article/REST")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SoftLayerApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SoftLayerApiMetadata build() {
+         return new SoftLayerApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerProviderMetadata.java b/providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerProviderMetadata.java
index 46c7377..00f465a 100644
--- a/providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerProviderMetadata.java
+++ b/providers/softlayer/src/main/java/org/jclouds/softlayer/SoftLayerProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.softlayer;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@ link org.jclouds.types.ProviderMetadata} for SoftLayer.
@@ -32,84 +28,36 @@
  * @author Adrian Cole
  */
 public class SoftLayerProviderMetadata extends BaseProviderMetadata {
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "softlayer";
+   public SoftLayerProviderMetadata() {
+      this(builder()
+            .id("softlayer")
+            .name("SoftLayer")
+            .api(new SoftLayerApiMetadata())
+            .homepage(URI.create("http://www.softlayer.com"))
+            .console(URI.create("https://manage.softlayer.com"))
+            .iso3166Codes("SG","US-CA","US-TX","US-VA","US-WA","US-TX"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SoftLayerProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "SoftLayer";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SoftLayerProviderMetadata build() {
+         return new SoftLayerProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "API Username";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "API Key";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.softlayer.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://manage.softlayer.com");
-   }
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://sldn.softlayer.com/article/REST");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("softlayer");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("SG","US-CA","US-TX","US-VA","US-WA","US-TX");
-   }
-
 }
\ No newline at end of file
diff --git a/providers/softlayer/src/test/java/org/jclouds/softlayer/SoftLayerProviderTest.java b/providers/softlayer/src/test/java/org/jclouds/softlayer/SoftLayerProviderTest.java
index 4f235dc..01d71eb 100644
--- a/providers/softlayer/src/test/java/org/jclouds/softlayer/SoftLayerProviderTest.java
+++ b/providers/softlayer/src/test/java/org/jclouds/softlayer/SoftLayerProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.softlayer;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +29,6 @@
 public class SoftLayerProviderTest extends BaseProviderMetadataTest {
 
    public SoftLayerProviderTest() {
-      super(new SoftLayerProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new SoftLayerProviderMetadata(), new SoftLayerApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/stratogen-vcloud-mycloud/src/main/java/org/jclouds/stratogen/vcloud/mycloud/StratoGenVCloudMyCloudProviderMetadata.java b/providers/stratogen-vcloud-mycloud/src/main/java/org/jclouds/stratogen/vcloud/mycloud/StratoGenVCloudMyCloudProviderMetadata.java
index 6ff8beb..56d792b 100644
--- a/providers/stratogen-vcloud-mycloud/src/main/java/org/jclouds/stratogen/vcloud/mycloud/StratoGenVCloudMyCloudProviderMetadata.java
+++ b/providers/stratogen-vcloud-mycloud/src/main/java/org/jclouds/stratogen/vcloud/mycloud/StratoGenVCloudMyCloudProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.stratogen.vcloud.mycloud;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
+import org.jclouds.vcloud.VCloudApiMetadata;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for StratoGen VMware hosting
@@ -33,84 +30,37 @@
  */
 public class StratoGenVCloudMyCloudProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "stratogen-vcloud-mycloud";
+   public StratoGenVCloudMyCloudProviderMetadata() {
+      this(builder()
+            .id("stratogen-vcloud-mycloud")
+            .name("StratoGen VMware hosting")
+            .api(new VCloudApiMetadata())
+            .homepage(URI.create("http://www.stratogen.net"))
+            .console(URI.create("https://mycloud.stratogen.net/cloud/org/YOUR_ORG_HERE"))
+            .iso3166Codes("GB"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected StratoGenVCloudMyCloudProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "StratoGen VMware hosting";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public StratoGenVCloudMyCloudProviderMetadata build() {
+         return new StratoGenVCloudMyCloudProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "User at Organization (user@org)";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Password";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.stratogen.net");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://mycloud.stratogen.net/cloud/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://www.vmware.com/support/pubs/vcd_pubs.html");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("stratogen-vcloud-mycloud");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("GB");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
diff --git a/providers/stratogen-vcloud-mycloud/src/test/java/org/jclouds/stratogen/vcloud/mycloud/StratoGenVCloudMyCloudProviderTest.java b/providers/stratogen-vcloud-mycloud/src/test/java/org/jclouds/stratogen/vcloud/mycloud/StratoGenVCloudMyCloudProviderTest.java
index bbeb2d9..09ea912 100644
--- a/providers/stratogen-vcloud-mycloud/src/test/java/org/jclouds/stratogen/vcloud/mycloud/StratoGenVCloudMyCloudProviderTest.java
+++ b/providers/stratogen-vcloud-mycloud/src/test/java/org/jclouds/stratogen/vcloud/mycloud/StratoGenVCloudMyCloudProviderTest.java
@@ -19,7 +19,7 @@
 package org.jclouds.stratogen.vcloud.mycloud;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.vcloud.VCloudApiMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +30,6 @@
 public class StratoGenVCloudMyCloudProviderTest extends BaseProviderMetadataTest {
 
    public StratoGenVCloudMyCloudProviderTest() {
-      super(new StratoGenVCloudMyCloudProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new StratoGenVCloudMyCloudProviderMetadata(), new VCloudApiMetadata());
    }
 }
diff --git a/providers/synaptic-storage/src/main/java/org/jclouds/synaptic/storage/SynapticStorageProviderMetadata.java b/providers/synaptic-storage/src/main/java/org/jclouds/synaptic/storage/SynapticStorageProviderMetadata.java
index 019c59d..fdd0075 100644
--- a/providers/synaptic-storage/src/main/java/org/jclouds/synaptic/storage/SynapticStorageProviderMetadata.java
+++ b/providers/synaptic-storage/src/main/java/org/jclouds/synaptic/storage/SynapticStorageProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.synaptic.storage;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.atmos.AtmosApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for AT&T's
@@ -34,76 +31,37 @@
  */
 public class SynapticStorageProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "synaptic-storage";
+   public SynapticStorageProviderMetadata() {
+      this(builder()
+            .id("synaptic-storage")
+            .name("AT&T Synaptic Storage")
+            .api(new AtmosApiMetadata())
+            .homepage(URI.create("https://www.synaptic.att.com/"))
+            .console(URI.create("https://www.synaptic.att.com/clouduser/login.htm"))
+            .iso3166Codes("US-VA", "US-TX"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.BLOBSTORE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected SynapticStorageProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "AT&T Synaptic Storage";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public SynapticStorageProviderMetadata build() {
+         return new SynapticStorageProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "Subtenant ID (UID)";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Shared Secret";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://www.synaptic.att.com/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://www.synaptic.att.com/clouduser/login.htm");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("https://www.synaptic.att.com/clouduser/emc_atmos_api.htm");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-VA", "US-TX");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
\ No newline at end of file
diff --git a/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/SynapticStorageProviderTest.java b/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/SynapticStorageProviderTest.java
index 897c37a..be9da98 100644
--- a/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/SynapticStorageProviderTest.java
+++ b/providers/synaptic-storage/src/test/java/org/jclouds/synaptic/storage/SynapticStorageProviderTest.java
@@ -18,8 +18,8 @@
  */
 package org.jclouds.synaptic.storage;
 
+import org.jclouds.atmos.AtmosApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,7 +31,7 @@
 public class SynapticStorageProviderTest extends BaseProviderMetadataTest {
 
    public SynapticStorageProviderTest() {
-      super(new SynapticStorageProviderMetadata(), ProviderMetadata.BLOBSTORE_TYPE);
+      super(new SynapticStorageProviderMetadata(), new AtmosApiMetadata());
    }
 
 }
\ No newline at end of file
diff --git a/providers/trmk-ecloud/src/main/java/org/jclouds/trmk/ecloud/TerremarkECloudApiMetadata.java b/providers/trmk-ecloud/src/main/java/org/jclouds/trmk/ecloud/TerremarkECloudApiMetadata.java
new file mode 100644
index 0000000..77b324a
--- /dev/null
+++ b/providers/trmk-ecloud/src/main/java/org/jclouds/trmk/ecloud/TerremarkECloudApiMetadata.java
@@ -0,0 +1,50 @@
+package org.jclouds.trmk.ecloud;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for Terremark eCloud v2.8 API
+ * 
+ * @author Adrian Cole
+ */
+public class TerremarkECloudApiMetadata extends BaseApiMetadata {
+
+   public TerremarkECloudApiMetadata() {
+      this(builder()
+            .id("trmk-ecloud")
+            .type(ApiType.COMPUTE)
+            .name("Terremark Enterprise Cloud v2.8 API")
+            .identityName("Email")
+            .credentialName("Password")
+            .documentation(URI.create("http://support.theenterprisecloud.com/kb/default.asp?id=533&Lang=1&SID=")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected TerremarkECloudApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public TerremarkECloudApiMetadata build() {
+         return new TerremarkECloudApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/providers/trmk-ecloud/src/main/java/org/jclouds/trmk/ecloud/TerremarkECloudProviderMetadata.java b/providers/trmk-ecloud/src/main/java/org/jclouds/trmk/ecloud/TerremarkECloudProviderMetadata.java
index c69ce27..2cb542f 100644
--- a/providers/trmk-ecloud/src/main/java/org/jclouds/trmk/ecloud/TerremarkECloudProviderMetadata.java
+++ b/providers/trmk-ecloud/src/main/java/org/jclouds/trmk/ecloud/TerremarkECloudProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.trmk.ecloud;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Terremark's Enterprise Cloud.
@@ -32,77 +28,36 @@
  * @author Adrian Cole
  */
 public class TerremarkECloudProviderMetadata extends BaseProviderMetadata {
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "trmk-ecloud";
+   public TerremarkECloudProviderMetadata() {
+      this(builder()
+            .id("trmk-ecloud")
+            .name("Terremark Enterprise Cloud v2.8")
+            .api(new TerremarkECloudApiMetadata())
+            .homepage(URI.create("http://www.terremark.com/services/cloudcomputing/theenterprisecloud.aspx"))
+            .console(URI.create("https://icenter.digitalops.net"))
+            .iso3166Codes("US-FL", "US-VA", "NL-NH", "BR-SP"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected TerremarkECloudProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Terremark Enterprise Cloud";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public TerremarkECloudProviderMetadata build() {
+         return new TerremarkECloudProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "email";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "password";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("http://www.terremark.com/services/cloudcomputing/theenterprisecloud.aspx");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://icenter.digitalops.net");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://support.theenterprisecloud.com/kb/default.asp?id=533&Lang=1&SID=");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.<String> of("US-FL", "US-VA", "NL-NH", "BR-SP");
-   }
-
 }
\ No newline at end of file
diff --git a/providers/trmk-ecloud/src/test/java/org/jclouds/trmk/ecloud/TerremarkECloudProviderTest.java b/providers/trmk-ecloud/src/test/java/org/jclouds/trmk/ecloud/TerremarkECloudProviderTest.java
index 5b9b07b..e3b13fb 100644
--- a/providers/trmk-ecloud/src/test/java/org/jclouds/trmk/ecloud/TerremarkECloudProviderTest.java
+++ b/providers/trmk-ecloud/src/test/java/org/jclouds/trmk/ecloud/TerremarkECloudProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.trmk.ecloud;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,6 +30,6 @@
 public class TerremarkECloudProviderTest extends BaseProviderMetadataTest {
 
    public TerremarkECloudProviderTest() {
-      super(new TerremarkECloudProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new TerremarkECloudProviderMetadata(), new TerremarkECloudApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/trmk-vcloudexpress/src/main/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressApiMetadata.java b/providers/trmk-vcloudexpress/src/main/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressApiMetadata.java
new file mode 100644
index 0000000..8362bd9
--- /dev/null
+++ b/providers/trmk-vcloudexpress/src/main/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressApiMetadata.java
@@ -0,0 +1,50 @@
+package org.jclouds.trmk.vcloudexpress;
+
+import java.net.URI;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.ApiType;
+import org.jclouds.apis.BaseApiMetadata;
+
+/**
+ * Implementation of {@link ApiMetadata} for the Terremark vCloud Express API
+ * 
+ * @author Adrian Cole
+ */
+public class TerremarkVCloudExpressApiMetadata extends BaseApiMetadata {
+
+   public TerremarkVCloudExpressApiMetadata() {
+      this(builder()
+            .id("trmk-vcloudexpress")
+            .type(ApiType.COMPUTE)
+            .name("Terremark vCloud Express API")
+            .identityName("Email")
+            .credentialName("Password")
+            .documentation(URI.create("https://community.vcloudexpress.terremark.com/en-us/product_docs/m/vcefiles/2342.aspx")));
+   }
+
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected TerremarkVCloudExpressApiMetadata(Builder<?> builder) {
+      super(builder);
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public TerremarkVCloudExpressApiMetadata build() {
+         return new TerremarkVCloudExpressApiMetadata(this);
+      }
+   }
+
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public ConcreteBuilder toBuilder() {
+      return builder().fromApiMetadata(this);
+   }
+
+}
\ No newline at end of file
diff --git a/providers/trmk-vcloudexpress/src/main/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressProviderMetadata.java b/providers/trmk-vcloudexpress/src/main/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressProviderMetadata.java
index 8cc2cad..a9a58dd 100644
--- a/providers/trmk-vcloudexpress/src/main/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressProviderMetadata.java
+++ b/providers/trmk-vcloudexpress/src/main/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressProviderMetadata.java
@@ -19,12 +19,8 @@
 package org.jclouds.trmk.vcloudexpress;
 
 import java.net.URI;
-import java.util.Set;
 
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Terremark's vCloud Express.
@@ -32,77 +28,37 @@
  * @author Adrian Cole
  */
 public class TerremarkVCloudExpressProviderMetadata extends BaseProviderMetadata {
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "trmk-vcloudexpress";
+   public TerremarkVCloudExpressProviderMetadata() {
+      this(builder()
+            .id("trmk-vcloudexpress")
+            .name("Terremark vCloud Express")
+            .api(new TerremarkVCloudExpressApiMetadata())
+            .homepage(URI.create("https://vcloudexpress.terremark.com/"))
+            .console(URI.create("https://my.vcloudexpress.terremark.com"))
+            .iso3166Codes("US-FL"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected TerremarkVCloudExpressProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "email";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public TerremarkVCloudExpressProviderMetadata build() {
+         return new TerremarkVCloudExpressProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "password";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "Terremark vCloud Express";
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://vcloudexpress.terremark.com/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://my.vcloudexpress.terremark.com");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("https://community.vcloudexpress.terremark.com/en-us/product_docs/m/vcefiles/2342.aspx");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-FL");
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
 
 }
\ No newline at end of file
diff --git a/providers/trmk-vcloudexpress/src/test/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressProviderTest.java b/providers/trmk-vcloudexpress/src/test/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressProviderTest.java
index 8a396e8..973b272 100644
--- a/providers/trmk-vcloudexpress/src/test/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressProviderTest.java
+++ b/providers/trmk-vcloudexpress/src/test/java/org/jclouds/trmk/vcloudexpress/TerremarkVCloudExpressProviderTest.java
@@ -19,7 +19,6 @@
 package org.jclouds.trmk.vcloudexpress;
 
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -31,6 +30,6 @@
 public class TerremarkVCloudExpressProviderTest extends BaseProviderMetadataTest {
 
    public TerremarkVCloudExpressProviderTest() {
-      super(new TerremarkVCloudExpressProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new TerremarkVCloudExpressProviderMetadata(), new TerremarkVCloudExpressApiMetadata());
    }
 }
\ No newline at end of file
diff --git a/providers/trystack-nova/src/main/java/org/jclouds/trystack/nova/TryStackNovaProviderMetadata.java b/providers/trystack-nova/src/main/java/org/jclouds/trystack/nova/TryStackNovaProviderMetadata.java
index dc32ad9..3c508fe 100644
--- a/providers/trystack-nova/src/main/java/org/jclouds/trystack/nova/TryStackNovaProviderMetadata.java
+++ b/providers/trystack-nova/src/main/java/org/jclouds/trystack/nova/TryStackNovaProviderMetadata.java
@@ -19,12 +19,9 @@
 package org.jclouds.trystack.nova;
 
 import java.net.URI;
-import java.util.Set;
 
+import org.jclouds.openstack.nova.v1_1.NovaApiMetadata;
 import org.jclouds.providers.BaseProviderMetadata;
-import org.jclouds.providers.ProviderMetadata;
-
-import com.google.common.collect.ImmutableSet;
 
 
 /**
@@ -34,84 +31,36 @@
  */
 public class TryStackNovaProviderMetadata extends BaseProviderMetadata {
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getId() {
-      return "trystack-nova";
+   public TryStackNovaProviderMetadata() {
+      this(builder()
+            .id("trystack-nova")
+            .name("TryStack.org (Nova)")
+            .api(new NovaApiMetadata())
+            .homepage(URI.create("https://trystack.org"))
+            .console(URI.create("https://trystack.org/dash"))
+            .iso3166Codes("US-CA"));
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getType() {
-      return ProviderMetadata.COMPUTE_TYPE;
+   // below are so that we can reuse builders, toString, hashCode, etc.
+   // we have to set concrete classes here, as our base class cannot be
+   // concrete due to serviceLoader
+   protected TryStackNovaProviderMetadata(ConcreteBuilder builder) {
+      super(builder);
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getIdentityName() {
-      return "tenantId:user";
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+
+      @Override
+      public TryStackNovaProviderMetadata build() {
+         return new TryStackNovaProviderMetadata(this);
+      }
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getCredentialName() {
-      return "Password";
+   public static ConcreteBuilder builder() {
+      return new ConcreteBuilder();
    }
 
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public String getName() {
-      return "HP Cloud Compute Services";
+   public ConcreteBuilder toBuilder() {
+      return builder().fromProviderMetadata(this);
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getHomepage() {
-      return URI.create("https://trystack.org");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getConsole() {
-      return URI.create("https://trystack.org/dash");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public URI getApiDocumentation() {
-      return URI.create("http://api.openstack.org/");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getLinkedServices() {
-      return ImmutableSet.of("trystack-nova");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public Set<String> getIso3166Codes() {
-      return ImmutableSet.of("US-CA");
-   }
-
 }
diff --git a/providers/trystack-nova/src/test/java/org/jclouds/trystack/nova/TryStackNovaProviderTest.java b/providers/trystack-nova/src/test/java/org/jclouds/trystack/nova/TryStackNovaProviderTest.java
index ce95da3..3971fad 100644
--- a/providers/trystack-nova/src/test/java/org/jclouds/trystack/nova/TryStackNovaProviderTest.java
+++ b/providers/trystack-nova/src/test/java/org/jclouds/trystack/nova/TryStackNovaProviderTest.java
@@ -35,8 +35,8 @@
  */
 package org.jclouds.trystack.nova;
 
+import org.jclouds.openstack.nova.v1_1.NovaApiMetadata;
 import org.jclouds.providers.BaseProviderMetadataTest;
-import org.jclouds.providers.ProviderMetadata;
 import org.testng.annotations.Test;
 
 /**
@@ -47,6 +47,6 @@
 public class TryStackNovaProviderTest extends BaseProviderMetadataTest {
 
    public TryStackNovaProviderTest() {
-      super(new TryStackNovaProviderMetadata(), ProviderMetadata.COMPUTE_TYPE);
+      super(new TryStackNovaProviderMetadata(), new NovaApiMetadata());
    }
 }
diff --git a/sandbox-apis/nirvanix/src/test/java/org/jclouds/nirvanix/sdn/ProvidersInPropertiesTest.java b/sandbox-apis/nirvanix/src/test/java/org/jclouds/nirvanix/sdn/ProvidersInPropertiesTest.java
deleted file mode 100644
index c9a55a7..0000000
--- a/sandbox-apis/nirvanix/src/test/java/org/jclouds/nirvanix/sdn/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,47 +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.nirvanix.sdn;
-
-import org.jclouds.blobstore.util.BlobStoreUtils;
-import org.jclouds.rest.Providers;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "sdn") : providers;
-   }
-
-   @Test
-   public void testSupportedBlobStoreProviders() {
-      Iterable<String> providers = BlobStoreUtils.getSupportedProviders();
-      assert !Iterables.contains(providers, "sdn") : providers;
-   }
-
-}
diff --git a/sandbox-apis/pcs/src/test/java/org/jclouds/mezeo/pcs/ProvidersInPropertiesTest.java b/sandbox-apis/pcs/src/test/java/org/jclouds/mezeo/pcs/ProvidersInPropertiesTest.java
deleted file mode 100644
index 8e59019..0000000
--- a/sandbox-apis/pcs/src/test/java/org/jclouds/mezeo/pcs/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,47 +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.mezeo.pcs;
-
-import org.jclouds.blobstore.util.BlobStoreUtils;
-import org.jclouds.rest.Providers;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "pcs") : providers;
-   }
-
-   @Test
-   public void testSupportedBlobStoreProviders() {
-      Iterable<String> providers = BlobStoreUtils.getSupportedProviders();
-      assert !Iterables.contains(providers, "pcs") : providers;
-   }
-
-}
diff --git a/sandbox-providers/ibm-smartcloud/src/test/java/org/jclouds/ibm/smartcloud/ProvidersInPropertiesTest.java b/sandbox-providers/ibm-smartcloud/src/test/java/org/jclouds/ibm/smartcloud/ProvidersInPropertiesTest.java
deleted file mode 100644
index 5426d8d..0000000
--- a/sandbox-providers/ibm-smartcloud/src/test/java/org/jclouds/ibm/smartcloud/ProvidersInPropertiesTest.java
+++ /dev/null
@@ -1,47 +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.ibm.smartcloud;
-
-import org.jclouds.compute.util.ComputeServiceUtils;
-import org.jclouds.rest.Providers;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.Iterables;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Test(groups = "unit")
-public class ProvidersInPropertiesTest {
-
-   @Test
-   public void testSupportedProviders() {
-      Iterable<String> providers = Providers.getSupportedProviders();
-      assert Iterables.contains(providers, "ibm-smartcloud") : providers;
-   }
-
-   @Test
-   public void testSupportedComputeServiceProviders() {
-      Iterable<String> providers = ComputeServiceUtils.getSupportedProviders();
-      assert Iterables.contains(providers, "ibm-smartcloud") : providers;
-   }
-
-}
diff --git a/scriptbuilder/src/main/resources/functions/installOpenJDK.sh b/scriptbuilder/src/main/resources/functions/installOpenJDK.sh
index 0ac3b2f..8f24fec 100644
--- a/scriptbuilder/src/main/resources/functions/installOpenJDK.sh
+++ b/scriptbuilder/src/main/resources/functions/installOpenJDK.sh
@@ -19,18 +19,22 @@
 
 function installOpenJDK() {
   if hash apt-get 2>/dev/null; then
-    export pkg=openjdk-7-jdk
-    apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk-*|grep -v common`
+    pkg=openjdk-7-jdk
+    (apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )) &&
+      export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk*|grep -v common`
+    # ex. lucid where jdk 7 is not present
+    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
+    test -d $JAVA_HOME || apt-get-install openjdk-6-jdk
   elif hash yum 2>/dev/null; then
     #TODO: find a jdk7 yum repo
     export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg
+    yum --nogpgcheck -y install $pkg &&
     export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
   else
     abort "we only support apt-get and yum right now... please contribute!"
     return 1
   fi
+  test -n "$JAVA_HOME" || abort "JDK installation failed!"
   ln -Fs $JAVA_HOME /usr/local/jdk 
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
diff --git a/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh b/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh
index d35c30b..f88f8fcd 100644
--- a/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh
+++ b/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh
@@ -154,18 +154,22 @@
 
 function installOpenJDK() {
   if hash apt-get 2>/dev/null; then
-    export pkg=openjdk-7-jdk
-    apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk-*|grep -v common`
+    pkg=openjdk-7-jdk
+    (apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg )) &&
+      export JAVA_HOME=`ls -d /usr/lib/jvm/java-7-openjdk*|grep -v common`
+    # ex. lucid where jdk 7 is not present
+    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
+    test -d $JAVA_HOME || apt-get-install openjdk-6-jdk
   elif hash yum 2>/dev/null; then
     #TODO: find a jdk7 yum repo
     export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg
+    yum --nogpgcheck -y install $pkg &&
     export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
   else
     abort "we only support apt-get and yum right now... please contribute!"
     return 1
   fi
+  test -n "$JAVA_HOME" || abort "JDK installation failed!"
   ln -Fs $JAVA_HOME /usr/local/jdk 
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile