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

* 'master' of github.com:jclouds/jclouds: (30 commits)
  Merge Keystone optional admin API provider with optional extension provider
  Added binding for optional extensions
  Make the OpenStack extension lists accessible for all APIs. Copied the Nova extension related code to the shared keystone package and added providers for extensions and aliases to all modules
  added statement to rebase dist packages
  added VAppNetworksLiveTest
  Further OpenStack extensibility changes. Fixing constructors in RestContext classes
  Fixed a possible NPE when ServiceFunctionLoader finds no OSGi service for a function.
  fixed broken test
  threading issue in test
  openstack-quantum extensibility
  openstack-swift extensibility
  openstack-glance extensibility
  openstack-nova extensibility
  openstack-keystone extensibility
  add FirstNetwork, in line with FirstRegion, etc., for use in FGCP provider
  missing class
  refactoring of smartos-ssh
  Extend the VmSpecification to allow for all the extra memory settings.
  Issue 1067:update to guava 13
  removed invalid @Nullable usage and also usage of javax package usage
  ...
diff --git a/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextString.java b/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextString.java
index 45cb69b..0eb2ba0 100644
--- a/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextString.java
+++ b/apis/cloudsigma/src/main/java/org/jclouds/cloudsigma/binders/BindCloneDriveOptionsToPlainTextString.java
@@ -24,7 +24,6 @@
 
 import java.util.Map;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import javax.ws.rs.core.MediaType;
@@ -68,7 +67,7 @@
 
       request.setPayload(listOfMapsToListOfKeyValuesDelimitedByBlankLines.apply(ImmutableSet.of(Maps.transformValues(postParams, new Function<Object, String>() {
          @Override
-         public String apply(@Nullable Object input) {
+         public String apply(Object input) {
             return input == null ? null : input.toString();
          }
       }))));
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java
index 59a4418..904c83f 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java
@@ -27,7 +27,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
@@ -153,12 +152,12 @@
             filter(getIPForwardingRulesByVirtualMachine.getUnchecked(from.getId()),
                new Predicate<IPForwardingRule>() {
                   @Override
-                  public boolean apply(@Nullable IPForwardingRule rule) {
+                  public boolean apply(IPForwardingRule rule) {
                      return !"Deleting".equals(rule.getState());
                   }
                }), new Function<IPForwardingRule, String>() {
             @Override
-            public String apply(@Nullable IPForwardingRule rule) {
+            public String apply(IPForwardingRule rule) {
                return rule.getIPAddress();
             }
          }));
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/NetworkPredicates.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/NetworkPredicates.java
index 1241e4f..8c63ea4 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/NetworkPredicates.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/predicates/NetworkPredicates.java
@@ -20,8 +20,6 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.domain.GuestIPType;
 import org.jclouds.cloudstack.domain.Network;
 import org.jclouds.cloudstack.domain.NetworkService;
@@ -163,7 +161,7 @@
       }
 
       @Override
-      public boolean apply(@Nullable GuestIPType guestIPType) {
+      public boolean apply(GuestIPType guestIPType) {
          return guestIPType == this.guestIPType;
       }
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
index 8b190a0..bdd32ac 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
@@ -26,8 +26,6 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.FirewallRule;
 import org.jclouds.cloudstack.domain.Network;
@@ -69,7 +67,7 @@
          network = find(client.getNetworkClient().listNetworks(), Predicates.and(supportsPortForwarding(),
             new Predicate<Network>() {
                @Override
-               public boolean apply(@Nullable Network network) {
+               public boolean apply(Network network) {
                   return network.isDefault()
                      && !network.isSecurityGroupEnabled()
                      && network.getAccount().equals(user.getAccount());
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java
index 1d75e21..093ffe7 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java
@@ -24,8 +24,6 @@
 
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.domain.ConfigurationEntry;
 import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
@@ -97,7 +95,7 @@
    private ConfigurationEntry getEntryByName(Set<ConfigurationEntry> entries, final String name) {
       return Iterables.find(entries, new Predicate<ConfigurationEntry>() {
          @Override
-         public boolean apply(@Nullable ConfigurationEntry entry) {
+         public boolean apply(ConfigurationEntry entry) {
             return entry != null && Objects.equal(name, entry.getName());
          }
       });
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientLiveTest.java
index 08581e7..e47baf8 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientLiveTest.java
@@ -23,8 +23,6 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.domain.Domain;
 import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.BeforeMethod;
@@ -48,7 +46,7 @@
       domainClient = globalAdminClient.getDomainClient();
       rootDomain = find(domainClient.listDomains(), new Predicate<Domain>() {
          @Override
-         public boolean apply(@Nullable Domain domain) {
+         public boolean apply(Domain domain) {
             return domain != null && domain.getName().equals("ROOT");
          }
       });
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
index c7b4c9d..8d8d385 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
@@ -30,8 +30,6 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.domain.AsyncJob;
 import org.jclouds.cloudstack.domain.JobResult;
 import org.jclouds.cloudstack.domain.LoadBalancerRule;
@@ -78,7 +76,7 @@
                Predicates.and(hasLoadBalancerService(), isVirtualNetwork(),
                   new Predicate<Network>() {
                      @Override
-                     public boolean apply(@Nullable Network network) {
+                     public boolean apply(Network network) {
                         return network.isDefault()
                            && !network.isSecurityGroupEnabled()
                            && !network.isSystem()
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
index e9a8ce0..f4a3173 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
@@ -27,8 +27,6 @@
 import java.net.URLDecoder;
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.AsyncJob;
 import org.jclouds.cloudstack.domain.ExtractMode;
@@ -107,7 +105,7 @@
       Iterable<Network> networks = client.getNetworkClient().listNetworks(ListNetworksOptions.Builder.zoneId(zone.getId()).isDefault(true));
       networks = Iterables.filter(networks, new Predicate<Network>() {
          @Override
-         public boolean apply(@Nullable Network network) {
+         public boolean apply(Network network) {
             return network != null && network.getState().equals("Implemented");
          }
       });
@@ -160,7 +158,7 @@
       Iterable<Network> networks = client.getNetworkClient().listNetworks(ListNetworksOptions.Builder.zoneId(zone.getId()).isDefault(true));
       networks = Iterables.filter(networks, new Predicate<Network>() {
          @Override
-         public boolean apply(@Nullable Network network) {
+         public boolean apply(Network network) {
             return network != null && network.getName().equals("Virtual Network");
          }
       });
@@ -181,7 +179,7 @@
       final String zoneId = zone.getId();
       Predicate<Template> templateReadyPredicate = new Predicate<Template>() {
          @Override
-         public boolean apply(@Nullable Template template) {
+         public boolean apply(Template template) {
             if (template == null) return false;
             Template t2 = client.getTemplateClient().getTemplateInZone(template.getId(), zoneId);
             Logger.CONSOLE.info("%s", t2.getStatus());
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
index 3bf7bd4..8cc514e 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
@@ -34,8 +34,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.logging.Logger;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.CloudStackClient;
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.AsyncJob;
@@ -94,7 +92,7 @@
       if (networks.size() > 0) {
          Network network = get(filter(networks, new Predicate<Network>() {
             @Override
-            public boolean apply(@Nullable Network network) {
+            public boolean apply(Network network) {
                return network != null && network.getState().equals("Implemented");
             }
          }), 0);
@@ -143,7 +141,7 @@
       String zoneId = getFirst(networks, null).getZoneId();
       options.networkIds(Iterables.transform(networks, new Function<Network, String>() {
          @Override
-         public String apply(@Nullable Network network) {
+         public String apply(Network network) {
             return network.getId();
          }
       }));
@@ -240,7 +238,7 @@
          Network requiredNetwork = getOnlyElement(filter(adminClient.getNetworkClient().listNetworks(
             ListNetworksOptions.Builder.zoneId(template.getZoneId())), new Predicate<Network>() {
             @Override
-            public boolean apply(@Nullable Network network) {
+            public boolean apply(Network network) {
                return network.isDefault() &&
                   network.getGuestIPType() == GuestIPType.VIRTUAL;
             }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
index 0710ae3..f610ca2 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/StaticNATVirtualMachineInNetworkLiveTest.java
@@ -26,8 +26,6 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.domain.IPForwardingRule;
 import org.jclouds.cloudstack.domain.Network;
 import org.jclouds.cloudstack.domain.PublicIPAddress;
@@ -96,7 +94,7 @@
       rule = getOnlyElement(filter(client.getNATClient().getIPForwardingRulesForIPAddress(ip.getId()),
             new Predicate<IPForwardingRule>() {
                @Override
-               public boolean apply(@Nullable IPForwardingRule rule) {
+               public boolean apply(IPForwardingRule rule) {
                   return rule != null && rule.getStartPort() == 22;
                }
             }));
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/EC2AsyncClient.java b/apis/ec2/src/main/java/org/jclouds/ec2/EC2AsyncClient.java
index 01c4793..eeb138a 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/EC2AsyncClient.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/EC2AsyncClient.java
@@ -20,8 +20,6 @@
 
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.ec2.features.WindowsAsyncApi;
 import org.jclouds.ec2.services.AMIAsyncClient;
 import org.jclouds.ec2.services.AvailabilityZoneAndRegionAsyncClient;
@@ -31,6 +29,7 @@
 import org.jclouds.ec2.services.KeyPairAsyncClient;
 import org.jclouds.ec2.services.SecurityGroupAsyncClient;
 import org.jclouds.ec2.services.WindowsAsyncClient;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
 import org.jclouds.rest.annotations.Delegate;
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/EC2Client.java b/apis/ec2/src/main/java/org/jclouds/ec2/EC2Client.java
index 0b4d17f..ffc8218 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/EC2Client.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/EC2Client.java
@@ -21,8 +21,6 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.concurrent.Timeout;
 import org.jclouds.ec2.features.WindowsApi;
 import org.jclouds.ec2.services.AMIClient;
@@ -33,6 +31,7 @@
 import org.jclouds.ec2.services.KeyPairClient;
 import org.jclouds.ec2.services.SecurityGroupClient;
 import org.jclouds.ec2.services.WindowsClient;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
 import org.jclouds.rest.annotations.Delegate;
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedData.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedData.java
index a5aba1f..83c6256 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedData.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/WindowsLoginCredentialsFromEncryptedData.java
@@ -23,7 +23,6 @@
 import java.security.PrivateKey;
 import java.security.spec.KeySpec;
 
-import javax.annotation.Nullable;
 import javax.crypto.Cipher;
 import javax.inject.Inject;
 
@@ -32,6 +31,7 @@
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
 import org.jclouds.encryption.internal.Base64;
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Function;
 import com.google.common.base.Throwables;
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApi.java
index 659fc4d..0085f34 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApi.java
@@ -21,12 +21,16 @@
 import java.util.concurrent.TimeUnit;
 
 import org.jclouds.concurrent.Timeout;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.location.functions.ZoneToEndpoint;
 import org.jclouds.openstack.keystone.v2_0.domain.ApiMetadata;
 import org.jclouds.openstack.keystone.v2_0.features.ServiceApi;
 import org.jclouds.openstack.keystone.v2_0.features.TenantApi;
 import org.jclouds.openstack.keystone.v2_0.features.TokenApi;
 import org.jclouds.openstack.keystone.v2_0.features.UserApi;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
 import org.jclouds.rest.annotations.Delegate;
+import org.jclouds.rest.annotations.EndpointParam;
 
 import com.google.common.base.Optional;
 
@@ -54,6 +58,12 @@
    @Delegate
    ServiceApi getServiceApi();
 
+   /**
+    * Provides synchronous access to Extension features.
+    */
+   @Delegate
+   ExtensionApi getExtensionApi();
+
    /** 
     * Provides synchronous access to Token features 
     */
@@ -66,7 +76,6 @@
    @Delegate
    Optional<UserApi> getUserApi();
    
-
    /** 
     * Provides synchronous access to Tenant features 
     */
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncApi.java
index 50560fc..52ac90d 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneAsyncApi.java
@@ -27,6 +27,7 @@
 import org.jclouds.openstack.keystone.v2_0.features.TenantAsyncApi;
 import org.jclouds.openstack.keystone.v2_0.features.TokenAsyncApi;
 import org.jclouds.openstack.keystone.v2_0.features.UserAsyncApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.SelectJson;
@@ -61,6 +62,12 @@
    ServiceAsyncApi getServiceApi();
 
    /**
+    * Provides asynchronous access to Extension features.
+    */
+   @Delegate
+   ExtensionAsyncApi getExtensionApi();
+
+   /**
     * @see KeystoneApi#getTokenApi()
     */
    @Delegate
@@ -71,7 +78,6 @@
     */
    @Delegate
    Optional<UserAsyncApi> getUserApi();
-   
 
    /**
     * @see KeystoneApi#getTenantApi()
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java
index f3615eb..f473be2 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java
@@ -23,6 +23,8 @@
 import java.net.URI;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import javax.inject.Singleton;
 
@@ -41,11 +43,14 @@
 import org.jclouds.openstack.keystone.v2_0.features.TokenAsyncApi;
 import org.jclouds.openstack.keystone.v2_0.features.UserApi;
 import org.jclouds.openstack.keystone.v2_0.features.UserAsyncApi;
-import org.jclouds.openstack.keystone.v2_0.functions.PresentWhenAdminURLExistsForIdentityService;
 import org.jclouds.openstack.keystone.v2_0.handlers.KeystoneErrorHandler;
 import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURIFromAccessForTypeAndVersion;
 import org.jclouds.openstack.keystone.v2_0.suppliers.RegionIdToAdminURISupplier;
 import org.jclouds.openstack.v2_0.ServiceType;
+import org.jclouds.openstack.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
+import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet;
 import org.jclouds.openstack.v2_0.services.Identity;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.annotations.ApiVersion;
@@ -54,7 +59,13 @@
 import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 import com.google.common.reflect.TypeToken;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
@@ -70,17 +81,18 @@
          RestClientModule<S, A> {
 
    public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
-            .put(ServiceApi.class, ServiceAsyncApi.class).put(TokenApi.class, TokenAsyncApi.class)
-            .put(UserApi.class, UserAsyncApi.class).put(TenantApi.class, TenantAsyncApi.class).build();
+            .put(ServiceApi.class, ServiceAsyncApi.class)
+            .put(ExtensionApi.class, ExtensionAsyncApi.class)
+            .put(TokenApi.class, TokenAsyncApi.class)
+            .put(UserApi.class, UserAsyncApi.class)
+            .put(TenantApi.class, TenantAsyncApi.class)
+            .build();
 
-   @SuppressWarnings("unchecked")
    public KeystoneRestClientModule() {
-      super(TypeToken.class.cast(TypeToken.of(KeystoneApi.class)), TypeToken.class.cast(TypeToken
-               .of(KeystoneAsyncApi.class)), DELEGATE_MAP);
+      super(TypeToken.class.cast(TypeToken.of(KeystoneApi.class)), TypeToken.class.cast(TypeToken.of(KeystoneAsyncApi.class)), DELEGATE_MAP);
    }
 
-   protected KeystoneRestClientModule(TypeToken<S> syncApiType, TypeToken<A> asyncApiType,
-            Map<Class<?>, Class<?>> sync2Async) {
+   protected KeystoneRestClientModule(TypeToken<S> syncApiType, TypeToken<A> asyncApiType, Map<Class<?>, Class<?>> sync2Async) {
       super(syncApiType, asyncApiType, sync2Async);
    }
 
@@ -88,7 +100,6 @@
 
       @Override
       protected void configure() {
-         bind(ImplicitOptionalConverter.class).to(PresentWhenAdminURLExistsForIdentityService.class);
          install(new FactoryModuleBuilder().implement(RegionIdToAdminURISupplier.class,
                   RegionIdToAdminURIFromAccessForTypeAndVersion.class).build(RegionIdToAdminURISupplier.Factory.class));
       }
@@ -113,6 +124,31 @@
    }
 
    @Override
+   protected void configure() {
+      bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class);
+      super.configure();
+   }
+   
+   @Provides
+   @Singleton
+   public Multimap<URI, URI> aliases() {
+       return ImmutableMultimap.<URI, URI>builder()
+          .build();
+   }
+
+   @Provides
+   @Singleton
+   public LoadingCache<String, Set<? extends Extension>> provideExtensionsByZone(final javax.inject.Provider<KeystoneApi> keystoneApi) {
+      return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS)
+            .build(CacheLoader.from(Suppliers.memoize(new Supplier<Set<? extends Extension>>() {
+               @Override
+               public Set<? extends Extension> get() {
+                  return keystoneApi.get().getExtensionApi().listExtensions();
+               }
+            })));
+   }
+
+   @Override
    protected void bindErrorHandlers() {
       bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(KeystoneErrorHandler.class);
       bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(KeystoneErrorHandler.class);
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApi.java
index 7fc8709..3a42c4d 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApi.java
@@ -40,5 +40,5 @@
    /**
     * The operation returns a list of tenants which the current token provides access to.
     */
-   Set<Tenant> listTenants();
+   Set<? extends Tenant> listTenants();
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/ServiceAsyncApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/ServiceAsyncApi.java
index b32482f..c890d35 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/ServiceAsyncApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/ServiceAsyncApi.java
@@ -57,5 +57,5 @@
    @Path("/tenants")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Tenant>> listTenants();
+   ListenableFuture<? extends Set<? extends Tenant>> listTenants();
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TenantApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TenantApi.java
index f4b9e5b..68a0118 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TenantApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TenantApi.java
@@ -40,7 +40,7 @@
    /**
     * The operation returns a list of tenants which the current token provides access to.
     */
-   Set<Tenant> list();
+   Set<? extends Tenant> list();
 
    /**
     * Retrieve information about a tenant, by tenant ID
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TenantAsyncApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TenantAsyncApi.java
index e03a38a..43b1ebe 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TenantAsyncApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TenantAsyncApi.java
@@ -62,7 +62,7 @@
    @Path("/tenants")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Tenant>> list();
+   ListenableFuture<? extends Set<? extends Tenant>> list();
 
    /** @see TenantApi#get(String) */
    @GET
@@ -71,7 +71,7 @@
    @Path("/tenants/{tenantId}")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Tenant> get(@PathParam("tenantId") String tenantId);
+   ListenableFuture<? extends Tenant> get(@PathParam("tenantId") String tenantId);
 
    /** @see TenantApi#getByName(String) */
    @GET
@@ -80,6 +80,6 @@
    @Path("/tenants")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Tenant> getByName(@QueryParam("name") String tenantName);
+   ListenableFuture<? extends Tenant> getByName(@QueryParam("name") String tenantName);
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TokenApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TokenApi.java
index ebd9baa..9b14b2a 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TokenApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TokenApi.java
@@ -72,6 +72,6 @@
     *
     * @return the set of endpoints
     */
-   Set<Endpoint> listEndpointsForToken(String token);
+   Set<? extends Endpoint> listEndpointsForToken(String token);
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TokenAsyncApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TokenAsyncApi.java
index 3f53195..2473400 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TokenAsyncApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/TokenAsyncApi.java
@@ -64,7 +64,7 @@
    @Path("/tokens/{token}")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Token> get(@PathParam("token") String token);
+   ListenableFuture<? extends Token> get(@PathParam("token") String token);
 
    /** @see TokenApi#getUserOfToken(String) */
    @GET
@@ -73,7 +73,7 @@
    @Path("/tokens/{token}")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<User> getUserOfToken(@PathParam("token") String token);
+   ListenableFuture<? extends User> getUserOfToken(@PathParam("token") String token);
 
    /** @see TokenApi#isValid(String) */
    @HEAD
@@ -89,6 +89,6 @@
    @Path("/tokens/{token}/endpoints")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Endpoint>> listEndpointsForToken(@PathParam("token") String token);
+   ListenableFuture<? extends Set<? extends Endpoint>> listEndpointsForToken(@PathParam("token") String token);
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/UserApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/UserApi.java
index 88ab62e..619ecf0 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/UserApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/UserApi.java
@@ -48,7 +48,7 @@
     * 
     * @return the list of users
     */
-   Set<User> list();
+   Set<? extends User> list();
 
    /**
     * Retrieve information about a user, by user ID
@@ -73,13 +73,13 @@
     * 
     * @return the set of Roles granted to the user
     */
-   Set<Role> listRolesOfUser(String userId);
+   Set<? extends Role> listRolesOfUser(String userId);
 
    /**
     * List the roles a user has been granted on a specific tenant
     * 
     * @return the set of roles
     */
-   Set<Role> listRolesOfUserOnTenant(String userId, String tenantId);
+   Set<? extends Role> listRolesOfUserOnTenant(String userId, String tenantId);
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/UserAsyncApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/UserAsyncApi.java
index 7e3cf60..d7ba6a8 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/UserAsyncApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/features/UserAsyncApi.java
@@ -61,7 +61,7 @@
    @Path("/users")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<User>> list();
+   ListenableFuture<? extends Set<? extends User>> list();
 
    /** @see UserApi#get(String) */
    @GET
@@ -70,7 +70,7 @@
    @Path("/users/{userId}")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<User> get(@PathParam("userId") String userId);
+   ListenableFuture<? extends User> get(@PathParam("userId") String userId);
    
    /** @see UserApi#getByName(String) */
    @GET
@@ -79,7 +79,7 @@
    @Path("/users")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<User> getByName(@QueryParam("name") String userName);
+   ListenableFuture<? extends User> getByName(@QueryParam("name") String userName);
    
    /** @see UserApi#listRolesOfUser(String) */
    @GET
@@ -88,7 +88,7 @@
    @Path("/users/{userId}/roles")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Role>> listRolesOfUser(@PathParam("userId") String userId);
+   ListenableFuture<? extends Set<? extends Role>> listRolesOfUser(@PathParam("userId") String userId);
 
    /** @see UserApi#listRolesOfUserOnTenant(String, String) */
    @GET
@@ -97,5 +97,5 @@
    @Path("/tenants/{tenantId}/users/{userId}/roles")
    @RequestFilters(AuthenticateRequest.class)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Role>> listRolesOfUserOnTenant(@PathParam("userId") String userId, @PathParam("tenantId") String tenantId);
+   ListenableFuture<? extends Set<? extends Role>> listRolesOfUserOnTenant(@PathParam("userId") String userId, @PathParam("tenantId") String tenantId);
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/PresentWhenAdminURLExistsForIdentityService.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/PresentWhenAdminURLExistsForIdentityService.java
deleted file mode 100644
index a49a916..0000000
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/functions/PresentWhenAdminURLExistsForIdentityService.java
+++ /dev/null
@@ -1,46 +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.openstack.keystone.v2_0.functions;
-
-import javax.inject.Singleton;
-
-import org.jclouds.internal.ClassMethodArgsAndReturnVal;
-import org.jclouds.rest.functions.ImplicitOptionalConverter;
-
-import com.google.common.base.Optional;
-
-/**
- * 
- * @author Adrian Cole
- * 
- */
-@Singleton
-public class PresentWhenAdminURLExistsForIdentityService implements ImplicitOptionalConverter {
-
-   @Override
-   public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
-      //TODO: log and return absent when the admin url for identity service isn't available
-      return Optional.of(input.getReturnVal());
-   }
-
-   public String toString() {
-      return "presentWhenAdminURLExistsForIdentityService()";
-   }
-
-}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Extension.java
similarity index 96%
rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java
rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Extension.java
index e96d583..6def36a 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Extension.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.openstack.nova.v2_0.domain;
+package org.jclouds.openstack.v2_0.domain;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -26,8 +26,6 @@
 import java.util.Set;
 
 import org.jclouds.javax.annotation.Nullable;
-import org.jclouds.openstack.v2_0.domain.Link;
-import org.jclouds.openstack.v2_0.domain.Resource;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java
similarity index 91%
rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApi.java
rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java
index 08647ca..e8f746e 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionApi.java
@@ -16,13 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.openstack.nova.v2_0.features;
+package org.jclouds.openstack.v2_0.features;
 
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import org.jclouds.concurrent.Timeout;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.domain.Extension;
 
 /**
  * Provides asynchronous access to Extensions via their REST API.
@@ -42,7 +42,7 @@
     * 
     * @return all extensions
     */
-   Set<Extension> listExtensions();
+   Set<? extends Extension> listExtensions();
 
    /**
     * Extensions may also be queried individually by their unique alias.
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionAsyncApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionAsyncApi.java
similarity index 89%
rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionAsyncApi.java
rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionAsyncApi.java
index a2207e4..c6b957f 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ExtensionAsyncApi.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/features/ExtensionAsyncApi.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.openstack.nova.v2_0.features;
+package org.jclouds.openstack.v2_0.features;
 
 import java.util.Set;
 
@@ -27,7 +27,7 @@
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.domain.Extension;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.SelectJson;
@@ -59,7 +59,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/extensions")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Extension>> listExtensions();
+   ListenableFuture<? extends Set<? extends Extension>> listExtensions();
 
    /**
     * @see ExtensionApi#getExtensionByAlias
@@ -69,6 +69,6 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/extensions/{alias}")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Extension> getExtensionByAlias(@PathParam("alias") String id);
+   ListenableFuture<? extends Extension> getExtensionByAlias(@PathParam("alias") String id);
 
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpace.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpace.java
similarity index 92%
rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpace.java
rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpace.java
index 97b814e..5174ae9 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpace.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpace.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.openstack.nova.v2_0.functions;
+package org.jclouds.openstack.v2_0.functions;
 
 import java.net.URI;
 
@@ -25,7 +25,7 @@
 import javax.inject.Singleton;
 import javax.ws.rs.core.UriBuilder;
 
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.domain.Extension;
 
 import com.google.common.base.Function;
 
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java
new file mode 100644
index 0000000..6681f71
--- /dev/null
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java
@@ -0,0 +1,88 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.openstack.v2_0.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.internal.ClassMethodArgsAndReturnVal;
+import org.jclouds.openstack.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.predicates.ExtensionPredicates;
+import org.jclouds.rest.functions.ImplicitOptionalConverter;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+/**
+ * We use the annotation {@link org.jclouds.openstack.services.Extension} to
+ * bind a class that iimplements an extension API to an {@link Extension}.
+ * 
+ * @author Adrian Cole
+ * 
+ */
+public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet implements
+      ImplicitOptionalConverter {
+   private final LoadingCache<String, Set<? extends Extension>> extensions;
+   private final Multimap<URI, URI> aliases;
+   
+   @Inject
+   public PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet(
+         LoadingCache<String, Set<? extends Extension>> extensions,
+         Multimap<URI, URI> aliases) {
+      this.extensions = checkNotNull(extensions, "extensions");
+      this.aliases = aliases == null ? ImmutableMultimap.<URI, URI>of() : ImmutableMultimap.copyOf(aliases);
+   }
+
+   @Override
+   public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
+      Optional<org.jclouds.openstack.v2_0.services.Extension> ext = Optional.fromNullable(input.getClazz().getAnnotation(
+            org.jclouds.openstack.v2_0.services.Extension.class));
+      if (ext.isPresent()) {
+         URI namespace = URI.create(ext.get().namespace());
+         if (input.getArgs() == null || input.getArgs().length == 0) {
+	        if (Iterables.any(extensions.getUnchecked(""),
+	              ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace))))
+	           return Optional.of(input.getReturnVal());
+	     } else if (input.getArgs() != null && input.getArgs().length == 1) {
+	        if (Iterables.any(extensions.getUnchecked(checkNotNull(input.getArgs()[0], "arg[0] in %s", input).toString()),
+	              ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace))))
+	           return Optional.of(input.getReturnVal());
+         } else {
+            throw new RuntimeException(String.format("expecting zero or one args %s", input));
+         }
+         return Optional.absent();
+      } else {
+         // No extension annotation, should check whether to return absent
+	     return Optional.of(input.getReturnVal());
+      }
+   }
+
+   @Override
+   public String toString() {
+      return "presentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet()";
+   }
+
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicates.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicates.java
similarity index 96%
rename from apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicates.java
rename to apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicates.java
index 140cacc..228859e 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicates.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicates.java
@@ -16,14 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.openstack.nova.v2_0.predicates;
+package org.jclouds.openstack.v2_0.predicates;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.net.URI;
 import java.util.Collection;
 
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.domain.Extension;
 
 import com.google.common.base.Predicate;
 
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiExpectTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiExpectTest.java
index e51d72f..f5e63a2 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiExpectTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiExpectTest.java
@@ -50,7 +50,7 @@
                HttpResponse.builder().statusCode(200)
                         .payload(payloadFromResourceWithContentType("/tenant_list.json", APPLICATION_JSON)).build())
                .getServiceApi();
-      Set<Tenant> tenants = api.listTenants();
+      Set<? extends Tenant> tenants = api.listTenants();
       assertNotNull(tenants);
       assertFalse(tenants.isEmpty());
 
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiLiveTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiLiveTest.java
index 67125bf..b12c5ae 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiLiveTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/ServiceApiLiveTest.java
@@ -37,7 +37,7 @@
 
    public void testTenants() {
       ServiceApi api = keystoneContext.getApi().getServiceApi();
-      Set<Tenant> result = api.listTenants();
+      Set<? extends Tenant> result = api.listTenants();
       assertNotNull(result);
       assertFalse(result.isEmpty());
 
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TenantApiExpectTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TenantApiExpectTest.java
index c30cd16..094d657 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TenantApiExpectTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TenantApiExpectTest.java
@@ -56,7 +56,7 @@
                HttpResponse.builder().statusCode(200).payload(
                         payloadFromResourceWithContentType("/tenant_list.json", APPLICATION_JSON)).build())
                .getTenantApi().get();
-      Set<Tenant> tenants = api.list();
+      Set<? extends Tenant> tenants = api.list();
       assertNotNull(tenants);
       assertFalse(tenants.isEmpty());
 
@@ -74,7 +74,7 @@
             HttpResponse.builder().statusCode(200).payload(
                   payloadFromResourceWithContentType("/tenant_list_att.json", APPLICATION_JSON)).build())
             .getTenantApi().get();
-      Set<Tenant> tenants = api.list();
+      Set<? extends Tenant> tenants = api.list();
       assertNotNull(tenants);
       assertFalse(tenants.isEmpty());
 
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TenantApiLiveTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TenantApiLiveTest.java
index 3411e4e..8c09e41 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TenantApiLiveTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TenantApiLiveTest.java
@@ -38,7 +38,7 @@
 
    public void testTenants() {
       TenantApi api = keystoneContext.getApi().getTenantApi().get();
-      Set<Tenant> result = api.list();
+      Set<? extends Tenant> result = api.list();
       assertNotNull(result);
       assertFalse(result.isEmpty());
 
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiExpectTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiExpectTest.java
index 54f518d..9132077 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiExpectTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiExpectTest.java
@@ -138,7 +138,7 @@
             authenticatedGET().endpoint(endpoint + "/v2.0/tokens/XXXXXX/endpoints").build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/user_endpoints.json", APPLICATION_JSON)).build())
             .getTokenApi().get();
-      Set<Endpoint> endpoints = api.listEndpointsForToken("XXXXXX");
+      Set<? extends Endpoint> endpoints = api.listEndpointsForToken("XXXXXX");
       
       assertEquals(endpoints, ImmutableSet.of(
             Endpoint.builder().publicURL(URI.create("https://csnode.jclouds.org/v2.0/"))
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiLiveTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiLiveTest.java
index d4eb9cb..7328190 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiLiveTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/TokenApiLiveTest.java
@@ -82,7 +82,7 @@
    public void testTokenEndpoints() {
 
       TokenApi api = keystoneContext.getApi().getTokenApi().get();
-      Set<Endpoint> endpoints = api.listEndpointsForToken(token);
+      Set<? extends Endpoint> endpoints = api.listEndpointsForToken(token);
       assertNotNull(endpoints);
       assertFalse(endpoints.isEmpty());
 
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/UserApiExpectTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/UserApiExpectTest.java
index bf828b9..01eddef 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/UserApiExpectTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/UserApiExpectTest.java
@@ -56,7 +56,7 @@
             authenticatedGET().endpoint(endpoint + "/v2.0/users").build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/user_list.json", APPLICATION_JSON)).build())
             .getUserApi().get();
-      Set<User> users = api.list();
+      Set<? extends User> users = api.list();
       assertNotNull(users);
       assertFalse(users.isEmpty());
 
@@ -123,7 +123,7 @@
             authenticatedGET().endpoint(endpoint + "/v2.0/users/3f6c1c9ba993495ead7d2eb2192e284f/roles").build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/user_role_list.json", APPLICATION_JSON)).build())
             .getUserApi().get();
-      Set<Role> roles = api.listRolesOfUser("3f6c1c9ba993495ead7d2eb2192e284f");
+      Set<? extends Role> roles = api.listRolesOfUser("3f6c1c9ba993495ead7d2eb2192e284f");
       assertNotNull(roles);
       assertFalse(roles.isEmpty());
       assertEquals(roles, ImmutableSet.of(
@@ -154,7 +154,7 @@
             authenticatedGET().endpoint(endpoint + "/v2.0/users/3f6c1c9ba993495ead7d2eb2192e284f/roles").build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/user_tenant_role_list.json", APPLICATION_JSON)).build())
             .getUserApi().get();
-      Set<Role> roles = api.listRolesOfUser("3f6c1c9ba993495ead7d2eb2192e284f");
+      Set<? extends Role> roles = api.listRolesOfUser("3f6c1c9ba993495ead7d2eb2192e284f");
       assertNotNull(roles);
       assertFalse(roles.isEmpty());
       assertEquals(roles, ImmutableSet.of(
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/UserApiLiveTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/UserApiLiveTest.java
index 6ce1f07..6e9759a 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/UserApiLiveTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/features/UserApiLiveTest.java
@@ -41,7 +41,7 @@
    public void testUsers() {
 
       UserApi api = keystoneContext.getApi().getUserApi().get();
-      Set<User> users = api.list();
+      Set<? extends User> users = api.list();
       assertNotNull(users);
       assertFalse(users.isEmpty());
       for (User user : users) {
@@ -54,12 +54,12 @@
    public void testUserRolesOnTenant() {
 
       UserApi api = keystoneContext.getApi().getUserApi().get();
-      Set<User> users = api.list();
-      Set<Tenant> tenants = keystoneContext.getApi().getTenantApi().get().list();
+      Set<? extends User> users = api.list();
+      Set<? extends Tenant> tenants = keystoneContext.getApi().getTenantApi().get().list();
 
       for (User user : users) {
          for (Tenant tenant : tenants) {
-            Set<Role> roles = api.listRolesOfUserOnTenant(user.getId(), tenant.getId());
+            Set<? extends Role> roles = api.listRolesOfUserOnTenant(user.getId(), tenant.getId());
             for (Role role : roles) {
                assertNotNull(role.getId());
             }
@@ -72,7 +72,7 @@
 
       UserApi api = keystoneContext.getApi().getUserApi().get();
       for (User user : api.list()) {
-         Set<Role> roles = api.listRolesOfUser(user.getId());
+         Set<? extends Role> roles = api.listRolesOfUser(user.getId());
          for (Role role : roles) {
             assertNotNull(role.getId());
          }
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpaceTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpaceTest.java
similarity index 82%
rename from apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpaceTest.java
rename to apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpaceTest.java
index ff57b2c..2518e69 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/ExtensionToNameSpaceTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/ExtensionToNameSpaceTest.java
@@ -1,4 +1,4 @@
-package org.jclouds.openstack.nova.v2_0.functions;
+package org.jclouds.openstack.v2_0.functions;
 
 import static org.testng.Assert.assertEquals;
 
@@ -8,8 +8,7 @@
 import javax.ws.rs.core.UriBuilder;
 
 import org.jclouds.date.internal.SimpleDateFormatDateService;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
-import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces;
+import org.jclouds.openstack.v2_0.domain.Extension;
 import org.testng.annotations.Test;
 
 import com.sun.jersey.api.uri.UriBuilderImpl;
@@ -39,6 +38,6 @@
       assertEquals(fn.apply(Extension.builder().alias("security_groups").name("SecurityGroups").namespace(
                URI.create("https://docs.openstack.org/ext/securitygroups/api/v1.1")).updated(
                new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-07-21T00:00:00+00:00")).description(
-               "Security group support").build()), URI.create(ExtensionNamespaces.SECURITY_GROUPS));
+               "Security group support").build()), URI.create("http://docs.openstack.org/ext/securitygroups/api/v1.1"));
    }
 }
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java
similarity index 69%
rename from apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java
rename to apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java
index 6cf3a25..9fa985b 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSetTest.java
@@ -1,4 +1,4 @@
-package org.jclouds.openstack.nova.v2_0.functions;
+package org.jclouds.openstack.v2_0.functions;
 
 import static org.testng.Assert.assertEquals;
 
@@ -9,10 +9,9 @@
 
 import org.jclouds.date.internal.SimpleDateFormatDateService;
 import org.jclouds.internal.ClassMethodArgsAndReturnVal;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
-import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces;
-import org.jclouds.openstack.nova.v2_0.extensions.KeyPairAsyncApi;
 import org.jclouds.openstack.v2_0.ServiceType;
+import org.jclouds.openstack.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet;
 import org.jclouds.rest.annotations.Delegate;
 import org.testng.annotations.Test;
 
@@ -40,8 +39,8 @@
             new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-08-08T00:00:00+00:00")).description(
             "Keypair Support").build();
 
-   @org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.KEYPAIRS)
-   static interface KeyPairIPAsyncApi {
+   @org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, namespace = "http://docs.openstack.org/ext/keypairs/api/v1.1")
+   static interface KeyPairAsyncApi {
 
    }
 
@@ -50,7 +49,7 @@
             new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-06-16T00:00:00+00:00")).description(
             "Floating IPs support").build();
 
-   @org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.FLOATING_IPS)
+   @org.jclouds.openstack.v2_0.services.Extension(of = ServiceType.COMPUTE, namespace = "http://docs.openstack.org/ext/floating_ips/api/v1.1")
    static interface FloatingIPAsyncApi {
 
    }
@@ -68,27 +67,27 @@
    ClassMethodArgsAndReturnVal getFloatingIPExtension() throws SecurityException, NoSuchMethodException {
       return ClassMethodArgsAndReturnVal.builder().clazz(FloatingIPAsyncApi.class).method(
                NovaAsyncApi.class.getDeclaredMethod("getFloatingIPExtensionForZone", String.class)).args(
-               new Object[] { "expectedzone" }).returnVal("foo").build();
+               new Object[] { "zone" }).returnVal("foo").build();
    }
 
    ClassMethodArgsAndReturnVal getKeyPairExtension() throws SecurityException, NoSuchMethodException {
       return ClassMethodArgsAndReturnVal.builder().clazz(KeyPairAsyncApi.class).method(
                NovaAsyncApi.class.getDeclaredMethod("getKeyPairExtensionForZone", String.class)).args(
-               new Object[] { "expectedzone" }).returnVal("foo").build();
+               new Object[] { "zone" }).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());
+      assertEquals(whenExtensionsInZoneInclude("zone", keypairs, floatingIps).apply(getFloatingIPExtension()), Optional.of("foo"));
+      assertEquals(whenExtensionsInZoneInclude("zone", keypairs, floatingIps).apply(getKeyPairExtension()), Optional.of("foo"));
+      assertEquals(whenExtensionsInZoneInclude("zone", keypairs).apply(getFloatingIPExtension()), Optional.absent());
+      assertEquals(whenExtensionsInZoneInclude("zone", floatingIps).apply(getKeyPairExtension()), Optional.absent());
    }
    
    public void testZoneWithoutExtensionsReturnsAbsent() throws SecurityException, NoSuchMethodException {
-      assertEquals(whenExtensionsInclude(floatingIps).apply(
+      assertEquals(whenExtensionsInZoneInclude("zone", floatingIps).apply(
                getFloatingIPExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent());
-      assertEquals(whenExtensionsInclude(keypairs).apply(
+      assertEquals(whenExtensionsInZoneInclude("zone", keypairs).apply(
                getKeyPairExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent());
    }
 
@@ -105,22 +104,22 @@
       Multimap<URI, URI> aliases = ImmutableMultimap.of(keypairs.getNamespace(), keypairsWithDifferentNamespace
                .getNamespace());
 
-      assertEquals(whenExtensionsAndAliasesInclude(ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply(
+      assertEquals(whenExtensionsAndAliasesInZoneInclude("zone", ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply(
               getKeyPairExtension()), Optional.of("foo"));
-      assertEquals(whenExtensionsAndAliasesInclude(ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply(
+      assertEquals(whenExtensionsAndAliasesInZoneInclude("zone", ImmutableSet.of(keypairsWithDifferentNamespace), aliases).apply(
               getFloatingIPExtension()), Optional.absent());
 
    }
 
-   private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsInclude(
-            Extension... extensions) {
-      return whenExtensionsAndAliasesInclude(ImmutableSet.copyOf(extensions), ImmutableMultimap.<URI, URI> of());
+   private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsInZoneInclude(
+            String zone, Extension... extensions) {
+      return whenExtensionsAndAliasesInZoneInclude(zone, ImmutableSet.copyOf(extensions), ImmutableMultimap.<URI, URI> of());
    }
 
-   private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsAndAliasesInclude(
-            final Set<Extension> extensions, final Multimap<URI, URI> aliases) {
-      final LoadingCache<String, Set<Extension>> extensionsForZone = CacheBuilder.newBuilder().build(
-               CacheLoader.from(Functions.forMap(ImmutableMap.of("expectedzone", extensions, "differentzone",
+   private PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet whenExtensionsAndAliasesInZoneInclude(
+            String zone, final Set<Extension> extensions, final Multimap<URI, URI> aliases) {
+      final LoadingCache<String, Set<? extends Extension>> extensionsForZone = CacheBuilder.newBuilder().build(
+               CacheLoader.from(Functions.forMap(ImmutableMap.<String, Set<? extends Extension>>of(zone, extensions, "differentzone",
                         ImmutableSet.<Extension> of()))));
 
       PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet fn = Guice.createInjector(
@@ -130,12 +129,11 @@
                   }
 
                   @Provides
-                  LoadingCache<String, Set<Extension>> getExtensions() {
+                  LoadingCache<String, Set<? extends Extension>> getExtensions() {
                      return extensionsForZone;
                   }
 
                   @Provides
-                  @Named("openstack.nova.extensions")
                   Multimap<URI, URI> getAliases() {
                      return aliases;
                   }
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicatesTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicatesTest.java
similarity index 87%
rename from apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicatesTest.java
rename to apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicatesTest.java
index 02ad030..859d1a3 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ExtensionPredicatesTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/v2_0/predicates/ExtensionPredicatesTest.java
@@ -16,15 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.openstack.nova.v2_0.predicates;
+package org.jclouds.openstack.v2_0.predicates;
 
-import static org.jclouds.openstack.nova.v2_0.predicates.ExtensionPredicates.aliasEquals;
-import static org.jclouds.openstack.nova.v2_0.predicates.ExtensionPredicates.namespaceEquals;
+import static org.jclouds.openstack.v2_0.predicates.ExtensionPredicates.aliasEquals;
+import static org.jclouds.openstack.v2_0.predicates.ExtensionPredicates.namespaceEquals;
 
 import java.net.URI;
 
 import org.jclouds.date.internal.SimpleDateFormatDateService;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.domain.Extension;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApi.java
index 7d87c7a..35eb3db 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApi.java
@@ -39,10 +39,10 @@
 import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceApi;
 import org.jclouds.openstack.nova.v2_0.extensions.VolumeApi;
 import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeApi;
-import org.jclouds.openstack.nova.v2_0.features.ExtensionApi;
 import org.jclouds.openstack.nova.v2_0.features.FlavorApi;
 import org.jclouds.openstack.nova.v2_0.features.ImageApi;
 import org.jclouds.openstack.nova.v2_0.features.ServerApi;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -100,98 +100,98 @@
     * Provides synchronous access to Floating IP features.
     */
    @Delegate
-   Optional<FloatingIPApi> getFloatingIPExtensionForZone(
+   Optional<? extends FloatingIPApi> getFloatingIPExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Security Group features.
     */
    @Delegate
-   Optional<SecurityGroupApi> getSecurityGroupExtensionForZone(
+   Optional<? extends SecurityGroupApi> getSecurityGroupExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Key Pair features.
     */
    @Delegate
-   Optional<KeyPairApi> getKeyPairExtensionForZone(
+   Optional<? extends KeyPairApi> getKeyPairExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Host Administration features.
     */
    @Delegate
-   Optional<HostAdministrationApi> getHostAdministrationExtensionForZone(
+   Optional<? extends HostAdministrationApi> getHostAdministrationExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Simple Tenant Usage features.
     */
    @Delegate
-   Optional<SimpleTenantUsageApi> getSimpleTenantUsageExtensionForZone(
+   Optional<? extends SimpleTenantUsageApi> getSimpleTenantUsageExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Volume features.
     */
    @Delegate
-   Optional<VolumeApi> getVolumeExtensionForZone(
+   Optional<? extends VolumeApi> getVolumeExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Virtual Interface features.
     */
    @Delegate
-   Optional<VirtualInterfaceApi> getVirtualInterfaceExtensionForZone(
+   Optional<? extends VirtualInterfaceApi> getVirtualInterfaceExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Server Extra Data features.
     */
    @Delegate
-   Optional<ServerWithSecurityGroupsApi> getServerWithSecurityGroupsExtensionForZone(
+   Optional<? extends ServerWithSecurityGroupsApi> getServerWithSecurityGroupsExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Server Admin Actions features.
     */
    @Delegate
-   Optional<AdminActionsApi> getAdminActionsExtensionForZone(
+   Optional<? extends AdminActionsApi> getAdminActionsExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
  
    /**
     * Provides synchronous access to Aggregate features.
     */
    @Delegate
-   Optional<HostAggregateApi> getHostAggregateExtensionForZone(
+   Optional<? extends HostAggregateApi> getHostAggregateExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Flavor extra specs features.
     */
    @Delegate
-   Optional<FlavorExtraSpecsApi> getFlavorExtraSpecsExtensionForZone(
+   Optional<? extends FlavorExtraSpecsApi> getFlavorExtraSpecsExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Quota features.
     */
    @Delegate
-   Optional<QuotaApi> getQuotaExtensionForZone(
+   Optional<? extends QuotaApi> getQuotaExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Quota Classes features.
     */
    @Delegate
-   Optional<QuotaClassApi> getQuotaClassExtensionForZone(
+   Optional<? extends QuotaClassApi> getQuotaClassExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides synchronous access to Volume Type features.
     */
    @Delegate
-   Optional<VolumeTypeApi> getVolumeTypeExtensionForZone(
+   Optional<? extends VolumeTypeApi> getVolumeTypeExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaAsyncApi.java
index d379006..3e71a78 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaAsyncApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaAsyncApi.java
@@ -37,10 +37,10 @@
 import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.VolumeAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeAsyncApi;
-import org.jclouds.openstack.nova.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.openstack.nova.v2_0.features.FlavorAsyncApi;
 import org.jclouds.openstack.nova.v2_0.features.ImageAsyncApi;
 import org.jclouds.openstack.nova.v2_0.features.ServerAsyncApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -98,49 +98,49 @@
     * Provides asynchronous access to Floating IP features.
     */
    @Delegate
-   Optional<FloatingIPAsyncApi> getFloatingIPExtensionForZone(
+   Optional<? extends FloatingIPAsyncApi> getFloatingIPExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Security Group features.
     */
    @Delegate
-   Optional<SecurityGroupAsyncApi> getSecurityGroupExtensionForZone(
+   Optional<? extends SecurityGroupAsyncApi> getSecurityGroupExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Key Pair features.
     */
    @Delegate
-   Optional<KeyPairAsyncApi> getKeyPairExtensionForZone(
+   Optional<? extends KeyPairAsyncApi> getKeyPairExtensionForZone(
             @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Host Administration features.
     */
    @Delegate
-   Optional<HostAdministrationAsyncApi> getHostAdministrationExtensionForZone(
+   Optional<? extends HostAdministrationAsyncApi> getHostAdministrationExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Simple Tenant Usage features.
     */
    @Delegate
-   Optional<SimpleTenantUsageAsyncApi> getSimpleTenantUsageExtensionForZone(
+   Optional<? extends SimpleTenantUsageAsyncApi> getSimpleTenantUsageExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Volume features.
     */
    @Delegate
-   Optional<VolumeAsyncApi> getVolumeExtensionForZone(
+   Optional<? extends VolumeAsyncApi> getVolumeExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Virtual Interface features.
     */
    @Delegate
-   Optional<VirtualInterfaceAsyncApi> getVirtualInterfaceExtensionForZone(
+   Optional<? extends VirtualInterfaceAsyncApi> getVirtualInterfaceExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
 
@@ -148,49 +148,49 @@
     * Provides asynchronous access to Server Extra Data features.
     */
    @Delegate
-   Optional<ServerWithSecurityGroupsAsyncApi> getServerWithSecurityGroupsExtensionForZone(
+   Optional<? extends ServerWithSecurityGroupsAsyncApi> getServerWithSecurityGroupsExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Server Admin Actions features.
     */
    @Delegate
-   Optional<AdminActionsAsyncApi> getAdminActionsExtensionForZone(
+   Optional<? extends AdminActionsAsyncApi> getAdminActionsExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to HostAggregate features.
     */
    @Delegate
-   Optional<HostAggregateAsyncApi> getHostAggregateExtensionForZone(
+   Optional<? extends HostAggregateAsyncApi> getHostAggregateExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Flavor extra specs features.
     */
    @Delegate
-   Optional<FlavorExtraSpecsAsyncApi> getFlavorExtraSpecsExtensionForZone(
+   Optional<? extends FlavorExtraSpecsAsyncApi> getFlavorExtraSpecsExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Quota features.
     */
    @Delegate
-   Optional<QuotaAsyncApi> getQuotaExtensionForZone(
+   Optional<? extends QuotaAsyncApi> getQuotaExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Quota Classes features.
     */
    @Delegate
-   Optional<QuotaClassAsyncApi> getQuotaClassExtensionForZone(
+   Optional<? extends QuotaClassAsyncApi> getQuotaClassExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
    /**
     * Provides asynchronous access to Volume Type features.
     */
    @Delegate
-   Optional<VolumeTypeAsyncApi> getVolumeTypeExtensionForZone(
+   Optional<? extends VolumeTypeAsyncApi> getVolumeTypeExtensionForZone(
          @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone);
 
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java
index 622644b..f62ef07 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java
@@ -135,7 +135,7 @@
    }
 
    private void cleanupOrphanedSecurityGroupsInZone(Set<String> groups, String zoneId) {
-      Optional<SecurityGroupApi> securityGroupApi = novaApi.getSecurityGroupExtensionForZone(zoneId);
+      Optional<? extends SecurityGroupApi> securityGroupApi = novaApi.getSecurityGroupExtensionForZone(zoneId);
       if (securityGroupApi.isPresent()) {
          for (String group : groups) {
             for (SecurityGroup securityGroup : Iterables.filter(securityGroupApi.get().listSecurityGroups(),
@@ -152,7 +152,7 @@
    }
 
    private void cleanupOrphanedKeyPairsInZone(Set<String> groups, String zoneId) {
-      Optional<KeyPairApi> keyPairApi = novaApi.getKeyPairExtensionForZone(zoneId);
+      Optional<? extends KeyPairApi> keyPairApi = novaApi.getKeyPairExtensionForZone(zoneId);
       if (keyPairApi.isPresent()) {
          for (String group : groups) {
             for (Map<String, KeyPair> view : keyPairApi.get().listKeyPairs()) {
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
index de6e825..f2b5138 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
@@ -159,12 +159,12 @@
       Set<String> zones = zoneIds.get();
       checkState(zones.size() > 0, "no zones found in supplier %s", zoneIds);
       for (final String zoneId : zones) {
-         Set<Image> images = novaApi.getImageApiForZone(zoneId).listImagesInDetail();
+         Set<? extends Image> images = novaApi.getImageApiForZone(zoneId).listImagesInDetail();
          if (images.size() == 0) {
             logger.debug("no images found in zone %s", zoneId);
             continue;
          }
-         Iterable<Image> active = filter(images, ImagePredicates.statusEquals(Image.Status.ACTIVE));
+         Iterable<? extends Image> active = filter(images, ImagePredicates.statusEquals(Image.Status.ACTIVE));
          if (images.size() == 0) {
             logger.debug("no images with status active in zone %s; non-active: %s", zoneId,
                      transform(active, new Function<Image, String>() {
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java
index 250aa01..fb9aeb8 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CreateSecurityGroupIfNeeded.java
@@ -62,7 +62,7 @@
       checkNotNull(zoneSecurityGroupNameAndPorts, "zoneSecurityGroupNameAndPorts");
 
       String zoneId = zoneSecurityGroupNameAndPorts.getZone();
-      Optional<SecurityGroupApi> api = novaApi.getSecurityGroupExtensionForZone(zoneId);
+      Optional<? extends SecurityGroupApi> api = novaApi.getSecurityGroupExtensionForZone(zoneId);
       checkArgument(api.isPresent(), "Security groups are required, but the extension is not availablein zone %s!", zoneId);
       logger.debug(">> creating securityGroup %s", zoneSecurityGroupNameAndPorts);
       try {
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageToOperatingSystem.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageToOperatingSystem.java
index 5f9218f..56cc9cb 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageToOperatingSystem.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageToOperatingSystem.java
@@ -31,7 +31,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Named;
 
@@ -96,7 +95,7 @@
             try {
                osFamily = find(Arrays.asList(OsFamily.values()), new Predicate<OsFamily>() {
                   @Override
-                  public boolean apply(@Nullable OsFamily osFamily) {
+                  public boolean apply(OsFamily osFamily) {
                      return any(imageNameParts, equalTo(osFamily.name().toLowerCase()));
                   }
                });
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPair.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPair.java
index 49ae02b..6454419 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPair.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPair.java
@@ -59,7 +59,7 @@
       String zoneId = checkNotNull(zoneAndName, "zoneAndName").getZone();
       String prefix = zoneAndName.getName();
 
-      Optional<KeyPairApi> api = novaApi.getKeyPairExtensionForZone(zoneId);
+      Optional<? extends KeyPairApi> api = novaApi.getKeyPairExtensionForZone(zoneId);
       checkArgument(api.isPresent(), "Key pairs are required, but the extension is not available in zone %s!",
             zoneId);
 
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstance.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstance.java
index 146d097..578d0b1 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstance.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstance.java
@@ -51,7 +51,7 @@
    @Override
    public Iterable<FloatingIP> load(final ZoneAndId key) throws Exception {
       String zone = key.getZone();
-      Optional<FloatingIPApi> ipApiOptional = api.getFloatingIPExtensionForZone(zone);
+      Optional<? extends FloatingIPApi> ipApiOptional = api.getFloatingIPExtensionForZone(zone);
       if (ipApiOptional.isPresent()) {
          return Iterables.filter(ipApiOptional.get().listFloatingIPs(),
                   new Predicate<FloatingIP>() {
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java
index 3db26f9..0bef089 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java
@@ -18,6 +18,7 @@
  */
 package org.jclouds.openstack.nova.v2_0.config;
 
+import java.net.URI;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -29,47 +30,48 @@
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
 import org.jclouds.http.annotation.ServerError;
-import org.jclouds.openstack.nova.v2_0.NovaAsyncApi;
 import org.jclouds.openstack.nova.v2_0.NovaApi;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
-import org.jclouds.openstack.nova.v2_0.extensions.AdminActionsAsyncApi;
+import org.jclouds.openstack.nova.v2_0.NovaAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.AdminActionsApi;
-import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.AdminActionsAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces;
 import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsApi;
-import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.FlavorExtraSpecsAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi;
-import org.jclouds.openstack.nova.v2_0.extensions.HostAdministrationAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.HostAdministrationApi;
-import org.jclouds.openstack.nova.v2_0.extensions.HostAggregateAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.HostAdministrationAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.HostAggregateApi;
-import org.jclouds.openstack.nova.v2_0.extensions.KeyPairAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.HostAggregateAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.KeyPairApi;
-import org.jclouds.openstack.nova.v2_0.extensions.QuotaAsyncApi;
-import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassAsyncApi;
-import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassApi;
+import org.jclouds.openstack.nova.v2_0.extensions.KeyPairAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.QuotaApi;
-import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.QuotaAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassApi;
+import org.jclouds.openstack.nova.v2_0.extensions.QuotaClassAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
-import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsApi;
-import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.ServerWithSecurityGroupsAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageApi;
-import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.SimpleTenantUsageAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceApi;
-import org.jclouds.openstack.nova.v2_0.extensions.VolumeAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.VolumeApi;
-import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.VolumeAsyncApi;
 import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeApi;
-import org.jclouds.openstack.nova.v2_0.features.ExtensionAsyncApi;
-import org.jclouds.openstack.nova.v2_0.features.ExtensionApi;
-import org.jclouds.openstack.nova.v2_0.features.FlavorAsyncApi;
+import org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeAsyncApi;
 import org.jclouds.openstack.nova.v2_0.features.FlavorApi;
-import org.jclouds.openstack.nova.v2_0.features.ImageAsyncApi;
+import org.jclouds.openstack.nova.v2_0.features.FlavorAsyncApi;
 import org.jclouds.openstack.nova.v2_0.features.ImageApi;
-import org.jclouds.openstack.nova.v2_0.features.ServerAsyncApi;
+import org.jclouds.openstack.nova.v2_0.features.ImageAsyncApi;
 import org.jclouds.openstack.nova.v2_0.features.ServerApi;
-import org.jclouds.openstack.nova.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet;
+import org.jclouds.openstack.nova.v2_0.features.ServerAsyncApi;
 import org.jclouds.openstack.nova.v2_0.handlers.NovaErrorHandler;
+import org.jclouds.openstack.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
+import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.config.RestClientModule;
 import org.jclouds.rest.functions.ImplicitOptionalConverter;
@@ -78,6 +80,9 @@
 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.Multimap;
+import com.google.common.reflect.TypeToken;
 import com.google.inject.Provides;
 
 /**
@@ -86,11 +91,13 @@
  * @author Adrian Cole
  */
 @ConfiguresRestClient
-public class NovaRestClientModule extends RestClientModule<NovaApi, NovaAsyncApi> {
+public class NovaRestClientModule<S extends NovaApi, A extends NovaAsyncApi> extends RestClientModule<S, A> {
 
    public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
-         .put(ServerApi.class, ServerAsyncApi.class).put(FlavorApi.class, FlavorAsyncApi.class)
-         .put(ImageApi.class, ImageAsyncApi.class).put(ExtensionApi.class, ExtensionAsyncApi.class)
+         .put(ServerApi.class, ServerAsyncApi.class)
+         .put(FlavorApi.class, FlavorAsyncApi.class)
+         .put(ImageApi.class, ImageAsyncApi.class)
+         .put(ExtensionApi.class, ExtensionAsyncApi.class)
          .put(FloatingIPApi.class, FloatingIPAsyncApi.class)
          .put(SecurityGroupApi.class, SecurityGroupAsyncApi.class)
          .put(KeyPairApi.class, KeyPairAsyncApi.class)
@@ -106,30 +113,67 @@
          .put(QuotaClassApi.class, QuotaClassAsyncApi.class)
          .put(VolumeTypeApi.class, VolumeTypeAsyncApi.class)
          .build();
-
+   
    public NovaRestClientModule() {
-      super(DELEGATE_MAP);
+      super(TypeToken.class.cast(TypeToken.of(NovaApi.class)), TypeToken.class.cast(TypeToken.of(NovaAsyncApi.class)), DELEGATE_MAP);
+   }
+
+   protected NovaRestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType,
+            Map<Class<?>, Class<?>> sync2Async) {
+      super(syncClientType, asyncClientType, sync2Async);
    }
 
    @Override
    protected void configure() {
       install(new NovaParserModule());
-      bind(ImplicitOptionalConverter.class).to(
-            PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class);
+      bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class);
       super.configure();
    }
+   
+   @Provides
+   @Singleton
+   public Multimap<URI, URI> aliases() {
+       return ImmutableMultimap.<URI, URI>builder()
+          .put(URI.create(ExtensionNamespaces.SECURITY_GROUPS),
+               URI.create("http://docs.openstack.org/compute/ext/securitygroups/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.FLOATING_IPS),
+               URI.create("http://docs.openstack.org/compute/ext/floating_ips/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.KEYPAIRS),
+               URI.create("http://docs.openstack.org/compute/ext/keypairs/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.SIMPLE_TENANT_USAGE),
+               URI.create("http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.HOSTS),
+               URI.create("http://docs.openstack.org/compute/ext/hosts/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.VOLUMES),
+               URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.VIRTUAL_INTERFACES),
+               URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.CREATESERVEREXT),
+               URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.ADMIN_ACTIONS),
+               URI.create("http://docs.openstack.org/compute/ext/admin-actions/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.AGGREGATES),
+               URI.create("http://docs.openstack.org/compute/ext/aggregates/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.FLAVOR_EXTRA_SPECS),
+               URI.create("http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.QUOTAS),
+               URI.create("http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.QUOTA_CLASSES),
+               URI.create("http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1"))
+          .put(URI.create(ExtensionNamespaces.VOLUME_TYPES),
+               URI.create("http://docs.openstack.org/compute/ext/volume_types/api/v1.1"))
+          .build();
+   }
 
    @Provides
    @Singleton
-   public LoadingCache<String, Set<Extension>> provideExtensionsByZone(final Provider<NovaApi> novaApi) {
+   public LoadingCache<String, Set<? extends Extension>> provideExtensionsByZone(final Provider<NovaApi> novaApi) {
       return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS)
-            .build(new CacheLoader<String, Set<Extension>>() {
-
+            .build(new CacheLoader<String, Set<? extends Extension>>() {
                @Override
-               public Set<Extension> load(String key) throws Exception {
+               public Set<? extends Extension> load(String key) throws Exception {
                   return novaApi.get().getExtensionApiForZone(key).listExtensions();
                }
-
             });
    }
 
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPAsyncApi.java
index 3b31192..2c09f82 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPAsyncApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPAsyncApi.java
@@ -31,8 +31,8 @@
 
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
 import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
-import org.jclouds.openstack.nova.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.openstack.v2_0.ServiceType;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.openstack.v2_0.services.Extension;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.Payload;
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/KeyPairAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/KeyPairAsyncApi.java
index 2b62fa8..c433b71 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/KeyPairAsyncApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/extensions/KeyPairAsyncApi.java
@@ -32,8 +32,8 @@
 
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
 import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
-import org.jclouds.openstack.nova.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.openstack.v2_0.ServiceType;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.openstack.v2_0.services.Extension;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.Payload;
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorApi.java
index b9aec3d..5eb5cc6 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorApi.java
@@ -43,14 +43,14 @@
     * 
     * @return all flavors (IDs, names, links)
     */
-   Set<Resource> listFlavors();
+   Set<? extends Resource> listFlavors();
 
    /**
     * List all flavors (all details)
     * 
     * @return all flavors (all details)
     */
-   Set<Flavor> listFlavorsInDetail();
+   Set<? extends Flavor> listFlavorsInDetail();
 
    /**
     * List details of the specified flavor
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorAsyncApi.java
index ce154bf..632f888 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorAsyncApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/FlavorAsyncApi.java
@@ -61,7 +61,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/flavors")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Resource>> listFlavors();
+   ListenableFuture<? extends Set<? extends Resource>> listFlavors();
 
    /**
     * @see FlavorApi#listFlavorsInDetail
@@ -71,7 +71,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/flavors/detail")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Flavor>> listFlavorsInDetail();
+   ListenableFuture<? extends Set<? extends Flavor>> listFlavorsInDetail();
 
    /**
     * @see FlavorApi#getFlavor
@@ -81,6 +81,6 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/flavors/{id}")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Flavor> getFlavor(@PathParam("id") String id);
+   ListenableFuture<? extends Flavor> getFlavor(@PathParam("id") String id);
 
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ImageApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ImageApi.java
index 9ef8972..89fe3e3 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ImageApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ImageApi.java
@@ -43,14 +43,14 @@
     * 
     * @return all images (IDs, names, links)
     */
-   Set<Resource> listImages();
+   Set<? extends Resource> listImages();
 
    /**
     * List all images (all details)
     * 
     * @return all images (all details)
     */
-   Set<Image> listImagesInDetail();
+   Set<? extends Image> listImagesInDetail();
 
    /**
     * List details of the specified image
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ImageAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ImageAsyncApi.java
index 0682281..40fbc94 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ImageAsyncApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ImageAsyncApi.java
@@ -59,7 +59,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/images")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Resource>> listImages();
+   ListenableFuture<? extends Set<? extends Resource>> listImages();
 
    /**
     * @see ImageApi#listImagesInDetail
@@ -69,7 +69,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/images/detail")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Image>> listImagesInDetail();
+   ListenableFuture<? extends Set<? extends Image>> listImagesInDetail();
 
    /**
     * @see ImageApi#getImage
@@ -79,7 +79,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/images/{id}")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Image> getImage(@PathParam("id") String id);
+   ListenableFuture<? extends Image> getImage(@PathParam("id") String id);
 
    /**
     * @see ImageApi#deleteImage
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerApi.java
index 7d73f50..1378b18 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerApi.java
@@ -47,14 +47,14 @@
     * 
     * @return all servers (IDs, names, links)
     */
-   Set<Resource> listServers();
+   Set<? extends Resource> listServers();
 
    /**
     * List all servers (all details)
     * 
     * @return all servers (all details)
     */
-   Set<Server> listServersInDetail();
+   Set<? extends Server> listServersInDetail();
 
    /**
     * List details of the specified server
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerAsyncApi.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerAsyncApi.java
index 00355d8..c1d7a7b 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerAsyncApi.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/features/ServerAsyncApi.java
@@ -76,7 +76,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/servers")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Resource>> listServers();
+   ListenableFuture<? extends Set<? extends Resource>> listServers();
 
    /**
     * @see ServerApi#listServersInDetail
@@ -86,7 +86,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/servers/detail")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Server>> listServersInDetail();
+   ListenableFuture<? extends Set<? extends Server>> listServersInDetail();
 
    /**
     * @see ServerApi#getServer
@@ -96,7 +96,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/servers/{id}")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Server> getServer(@PathParam("id") String id);
+   ListenableFuture<? extends Server> getServer(@PathParam("id") String id);
 
    /**
     * @see ServerApi#deleteServer
@@ -144,7 +144,7 @@
    @Path("/servers/{id}/action")
    @Consumes
    @Produces(MediaType.APPLICATION_JSON)
-   @Payload("%7B\"resize\":%7B\"flavorId\":{flavorId}%7D%7D")
+   @Payload("%7B\"resize\":%7B\"flavorRef\":{flavorId}%7D%7D")
    ListenableFuture<Void> resizeServer(@PathParam("id") String id, @PayloadParam("flavorId") String flavorId);
 
    /**
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java
deleted file mode 100644
index 183d5c6..0000000
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/functions/PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.java
+++ /dev/null
@@ -1,113 +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.openstack.nova.v2_0.functions;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.net.URI;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-import org.jclouds.internal.ClassMethodArgsAndReturnVal;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
-import org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces;
-import org.jclouds.openstack.nova.v2_0.predicates.ExtensionPredicates;
-import org.jclouds.rest.functions.ImplicitOptionalConverter;
-
-import com.google.common.base.Optional;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-
-/**
- * We use the annotation {@link org.jclouds.openstack.services.Extension} to
- * bind a class that is an extension to an extension found in the
- * {@link org.jclouds.openstack.nova.v2_0.features.ExtensionApi#listExtensions} call.
- * 
- * @author Adrian Cole
- * 
- */
-@Singleton
-public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet implements
-      ImplicitOptionalConverter {
-   private final LoadingCache<String, Set<Extension>> extensions;
-
-   @com.google.inject.Inject(optional=true)
-   @Named("openstack.nova.extensions")
-   Multimap<URI, URI> aliases = ImmutableMultimap.<URI, URI>builder()
-      .put(URI.create(ExtensionNamespaces.SECURITY_GROUPS),
-           URI.create("http://docs.openstack.org/compute/ext/securitygroups/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.FLOATING_IPS),
-           URI.create("http://docs.openstack.org/compute/ext/floating_ips/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.KEYPAIRS),
-           URI.create("http://docs.openstack.org/compute/ext/keypairs/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.SIMPLE_TENANT_USAGE),
-           URI.create("http://docs.openstack.org/compute/ext/os-simple-tenant-usage/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.HOSTS),
-           URI.create("http://docs.openstack.org/compute/ext/hosts/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.VOLUMES),
-           URI.create("http://docs.openstack.org/compute/ext/volumes/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.VIRTUAL_INTERFACES),
-           URI.create("http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.CREATESERVEREXT),
-           URI.create("http://docs.openstack.org/compute/ext/createserverext/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.ADMIN_ACTIONS),
-           URI.create("http://docs.openstack.org/compute/ext/admin-actions/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.AGGREGATES),
-           URI.create("http://docs.openstack.org/compute/ext/aggregates/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.FLAVOR_EXTRA_SPECS),
-           URI.create("http://docs.openstack.org/compute/ext/flavor_extra_specs/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.QUOTAS),
-           URI.create("http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.QUOTA_CLASSES),
-           URI.create("http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1"))
-      .put(URI.create(ExtensionNamespaces.VOLUME_TYPES),
-           URI.create("http://docs.openstack.org/compute/ext/volume_types/api/v1.1"))
-      .build();
-   
-   @Inject
-   public PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet(
-         LoadingCache<String, Set<Extension>> extensions) {
-      this.extensions = checkNotNull(extensions, "extensions");
-   }
-
-   @Override
-   public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
-      Optional<org.jclouds.openstack.v2_0.services.Extension> ext = Optional.fromNullable(input.getClazz().getAnnotation(
-            org.jclouds.openstack.v2_0.services.Extension.class));
-      if (ext.isPresent()) {
-         checkState(input.getArgs() != null && input.getArgs().length == 1, "expecting an arg %s", input);
-         URI namespace = URI.create(ext.get().namespace());
-         if (Iterables.any(extensions.getUnchecked(checkNotNull(input.getArgs()[0], "arg[0] in %s", input).toString()),
-               ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace))))
-            return Optional.of(input.getReturnVal());
-      }
-      return Optional.absent();
-   }
-
-   public String toString() {
-      return "presentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet()";
-   }
-
-}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateVolumeOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateVolumeOptions.java
index 03422e1..097b0a9 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateVolumeOptions.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateVolumeOptions.java
@@ -46,12 +46,12 @@
    @Inject
    private BindToJsonPayload jsonBinder;
 
-   private String name;
-   private String description;
-   private String volumeType;
-   private String availabilityZone;
-   private String snapshotId;
-   private Map<String, String> metadata = ImmutableMap.of();
+   protected String name;
+   protected String description;
+   protected String volumeType;
+   protected String availabilityZone;
+   protected String snapshotId;
+   protected Map<String, String> metadata = ImmutableMap.of();
 
    @Override
    public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
@@ -61,6 +61,12 @@
          image.put("display_name", name);
       if (description != null)
          image.put("display_description", description);
+      if (volumeType != null)
+         image.put("volume_type", volumeType);
+      if (availabilityZone != null)
+         image.put("availability_zone", availabilityZone);
+      if (snapshotId != null)
+         image.put("snapshot_id", snapshotId);
       if (!metadata.isEmpty())
          image.put("metadata", metadata);
       return jsonBinder.bindToRequest(request, ImmutableMap.of("volume", image));
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java
index 0c9f6fd..0c435e3 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/FindSecurityGroupWithNameAndReturnTrue.java
@@ -62,7 +62,7 @@
       checkNotNull(securityGroupInZoneRef, "securityGroupRef");
       final ZoneAndName securityGroupInZone = checkNotNull(securityGroupInZoneRef.get(), "securityGroupInZone");
 
-      Optional<SecurityGroupApi> api = novaApi.getSecurityGroupExtensionForZone(securityGroupInZone.getZone());
+      Optional<? extends SecurityGroupApi> api = novaApi.getSecurityGroupExtensionForZone(securityGroupInZone.getZone());
       checkArgument(api.isPresent(), "Security groups are required, but the extension is not available!");
 
       logger.trace("looking for security group %s", securityGroupInZone.slashEncode());
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageToOperatingSystemTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageToOperatingSystemTest.java
index a84903b..1e837d3 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageToOperatingSystemTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageToOperatingSystemTest.java
@@ -26,8 +26,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.openstack.nova.v2_0.domain.Image;
@@ -69,7 +67,7 @@
       return Iterables.toArray(
             Iterables.transform(Arrays.asList(OsFamily.values()), new Function<OsFamily, Object[]>() {
                @Override
-               public Object[] apply(@Nullable OsFamily osFamily) {
+               public Object[] apply(OsFamily osFamily) {
                   return new Object[] { osFamily };
                }
             }), Object[].class);
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPairTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPairTest.java
index 0f86851..c2de758 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPairTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPairTest.java
@@ -52,7 +52,9 @@
 
       KeyPair pair = createMock(KeyPair.class);
 
-      expect(api.getKeyPairExtensionForZone("zone")).andReturn(Optional.of(keyApi)).atLeastOnce();
+      Optional optKeyApi = Optional.of(keyApi);
+      
+      expect(api.getKeyPairExtensionForZone("zone")).andReturn(optKeyApi).atLeastOnce();
 
       expect(keyApi.createKeyPair("group-1")).andReturn(pair);
 
@@ -83,7 +85,7 @@
 
       KeyPair pair = createMock(KeyPair.class);
 
-      expect(api.getKeyPairExtensionForZone("zone")).andReturn(Optional.of(keyApi)).atLeastOnce();
+      expect(api.getKeyPairExtensionForZone("zone")).andReturn((Optional) Optional.of(keyApi)).atLeastOnce();
 
       expect(uniqueIdSupplier.get()).andReturn("1");
       expect(keyApi.createKeyPair("group-1")).andThrow(new IllegalStateException());
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstanceTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstanceTest.java
index 4c32a2f..6a7e24c 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstanceTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/loaders/LoadFloatingIpsForInstanceTest.java
@@ -46,7 +46,7 @@
       FloatingIPApi ipApi = createMock(FloatingIPApi.class);
       FloatingIP testIp = FloatingIP.builder().id("1").ip("1.1.1.1").fixedIp("10.1.1.1").instanceId("i-blah").build();
 
-      expect(api.getFloatingIPExtensionForZone("Zone")).andReturn(Optional.of(ipApi)).atLeastOnce();
+      expect(api.getFloatingIPExtensionForZone("Zone")).andReturn((Optional) Optional.of(ipApi)).atLeastOnce();
       expect(ipApi.listFloatingIPs()).andReturn(ImmutableSet.<FloatingIP>of(testIp)).atLeastOnce();
 
       replay(api);
@@ -65,7 +65,7 @@
       NovaApi api = createMock(NovaApi.class);
       FloatingIPApi ipApi = createMock(FloatingIPApi.class);
 
-      expect(api.getFloatingIPExtensionForZone("Zone")).andReturn(Optional.of(ipApi)).atLeastOnce();
+      expect(api.getFloatingIPExtensionForZone("Zone")).andReturn((Optional) Optional.of(ipApi)).atLeastOnce();
 
       expect(ipApi.listFloatingIPs()).andReturn(ImmutableSet.<FloatingIP>of()).atLeastOnce();
 
@@ -86,7 +86,7 @@
       NovaApi api = createMock(NovaApi.class);
       FloatingIPApi ipApi = createMock(FloatingIPApi.class);
 
-      expect(api.getFloatingIPExtensionForZone("Zone")).andReturn(Optional.of(ipApi)).atLeastOnce();
+      expect(api.getFloatingIPExtensionForZone("Zone")).andReturn((Optional) Optional.of(ipApi)).atLeastOnce();
 
       expect(ipApi.listFloatingIPs()).andReturn(
             ImmutableSet.<FloatingIP>of(FloatingIP.builder().id("1").ip("1.1.1.1").build()))
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/AdminActionsApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/AdminActionsApiLiveTest.java
index f981afc..ae1bc4a 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/AdminActionsApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/AdminActionsApiLiveTest.java
@@ -27,11 +27,11 @@
 import org.jclouds.openstack.nova.v2_0.domain.BackupType;
 import org.jclouds.openstack.nova.v2_0.domain.Image;
 import org.jclouds.openstack.nova.v2_0.domain.Server.Status;
-import org.jclouds.openstack.nova.v2_0.features.ExtensionApi;
 import org.jclouds.openstack.nova.v2_0.features.ImageApi;
 import org.jclouds.openstack.nova.v2_0.features.ServerApi;
 import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiLiveTest;
 import org.jclouds.openstack.nova.v2_0.options.CreateBackupOfServerOptions;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeGroups;
@@ -53,7 +53,7 @@
    private ImageApi imageApi;
    private ServerApi serverApi;
    private ExtensionApi extensionApi;
-   private Optional<AdminActionsApi> apiOption;
+   private Optional<? extends AdminActionsApi> apiOption;
    private String zone;
 
    private String testServerId;
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/FlavorExtraSpecsApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/FlavorExtraSpecsApiLiveTest.java
index 6225888..a666aaf 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/FlavorExtraSpecsApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/FlavorExtraSpecsApiLiveTest.java
@@ -44,7 +44,7 @@
 @Test(groups = "live", testName = "FlavorExtraSpecsApiLiveTest", singleThreaded = true)
 public class FlavorExtraSpecsApiLiveTest extends BaseNovaApiLiveTest {
    private FlavorApi flavorApi;
-   private Optional<FlavorExtraSpecsApi> apiOption;
+   private Optional<? extends FlavorExtraSpecsApi> apiOption;
    private String zone;
 
    private Resource testFlavor;
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPApiLiveTest.java
index 9970056..8940b14 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/FloatingIPApiLiveTest.java
@@ -48,7 +48,7 @@
    @Test
    public void testListFloatingIPs() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
-         Optional<FloatingIPApi> apiOption = novaContext.getApi().getFloatingIPExtensionForZone(zoneId);
+         Optional<? extends FloatingIPApi> apiOption = novaContext.getApi().getFloatingIPExtensionForZone(zoneId);
          if (!apiOption.isPresent())
             continue;
          FloatingIPApi api = apiOption.get();
@@ -70,7 +70,7 @@
    @Test
    public void testAllocateAndDeallocateFloatingIPs() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
-         Optional<FloatingIPApi> apiOption = novaContext.getApi().getFloatingIPExtensionForZone(zoneId);
+         Optional<? extends FloatingIPApi> apiOption = novaContext.getApi().getFloatingIPExtensionForZone(zoneId);
          if (!apiOption.isPresent())
             continue;
          FloatingIPApi api = apiOption.get();
@@ -101,7 +101,7 @@
    @Test
    public void testAddAndRemoveFloatingIp() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
-         Optional<FloatingIPApi> apiOption = novaContext.getApi().getFloatingIPExtensionForZone(zoneId);
+         Optional<? extends FloatingIPApi> apiOption = novaContext.getApi().getFloatingIPExtensionForZone(zoneId);
          if (!apiOption.isPresent())
             continue;
          FloatingIPApi api = apiOption.get();
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/HostAdministrationApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/HostAdministrationApiLiveTest.java
index 6960a6f..88ff7b9 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/HostAdministrationApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/HostAdministrationApiLiveTest.java
@@ -42,7 +42,7 @@
  */
 @Test(groups = "live", testName = "HostAdministrationApiLiveTest", singleThreaded = true)
 public class HostAdministrationApiLiveTest extends BaseNovaApiLiveTest {
-   private Optional<HostAdministrationApi> optApi = Optional.absent();
+   private Optional<? extends HostAdministrationApi> optApi = Optional.absent();
 
    Predicate<Host> isComputeHost = new Predicate<Host>() {
       @Override
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/HostAggregateApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/HostAggregateApiLiveTest.java
index c089269..2224da4 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/HostAggregateApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/HostAggregateApiLiveTest.java
@@ -44,8 +44,8 @@
  */
 @Test(groups = "live", testName = "AggregateApiLiveTest", singleThreaded = true)
 public class HostAggregateApiLiveTest extends BaseNovaApiLiveTest {
-   private Optional<HostAggregateApi> apiOption;
-   private Optional<HostAdministrationApi> hostAdminOption;
+   private Optional<? extends HostAggregateApi> apiOption;
+   private Optional<? extends HostAdministrationApi> hostAdminOption;
 
    private HostAggregate testAggregate;
 
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/QuotaApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/QuotaApiLiveTest.java
index d1a1fe7..efd88d7 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/QuotaApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/QuotaApiLiveTest.java
@@ -36,7 +36,7 @@
  */
 @Test(groups = "live", testName = "QuotaApiLiveTest", singleThreaded = true)
 public class QuotaApiLiveTest extends BaseNovaApiLiveTest {
-   private Optional<QuotaApi> apiOption;
+   private Optional<? extends QuotaApi> apiOption;
    private String tenant;
 
    @BeforeClass(groups = {"integration", "live"})
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/QuotaClassApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/QuotaClassApiLiveTest.java
index 927ed4c..d5b80da 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/QuotaClassApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/QuotaClassApiLiveTest.java
@@ -36,7 +36,7 @@
  */
 @Test(groups = "live", testName = "QuotaClassApiLiveTest", singleThreaded = true)
 public class QuotaClassApiLiveTest extends BaseNovaApiLiveTest {
-   private Optional<QuotaClassApi> apiOption;
+   private Optional<? extends QuotaClassApi> apiOption;
    private String zone;
 
    @BeforeClass(groups = {"integration", "live"})
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/ServerWithSecurityGroupsApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/ServerWithSecurityGroupsApiLiveTest.java
index ebbaf78..533ac32 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/ServerWithSecurityGroupsApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/ServerWithSecurityGroupsApiLiveTest.java
@@ -41,7 +41,7 @@
 @Test(groups = "live", testName = "ServerWithSecurityGroupsApiLiveTest", singleThreaded = true)
 public class ServerWithSecurityGroupsApiLiveTest extends BaseNovaApiLiveTest {
    private ServerApi serverApi;
-   private Optional<ServerWithSecurityGroupsApi> apiOption;
+   private Optional<? extends ServerWithSecurityGroupsApi> apiOption;
    private String zone;
 
    @BeforeGroups(groups = {"integration", "live"})
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SimpleTenantUsageApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SimpleTenantUsageApiLiveTest.java
index 72d0a08..dfa2c66 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SimpleTenantUsageApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SimpleTenantUsageApiLiveTest.java
@@ -38,7 +38,7 @@
 
    public void testList() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
-         Optional<SimpleTenantUsageApi> optApi = novaContext.getApi().getSimpleTenantUsageExtensionForZone(zoneId);
+         Optional<? extends SimpleTenantUsageApi> optApi = novaContext.getApi().getSimpleTenantUsageExtensionForZone(zoneId);
          if (optApi.isPresent() && identity.endsWith(":admin")) {
             SimpleTenantUsageApi api = optApi.get();
             Set<SimpleTenantUsage> usages = api.listTenantUsages();
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VirtualInterfaceApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VirtualInterfaceApiLiveTest.java
index 8011a0e..1962a0f 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VirtualInterfaceApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VirtualInterfaceApiLiveTest.java
@@ -38,7 +38,7 @@
  */
 @Test(groups = "live", testName = "VirtualInterfaceApiLiveTest", singleThreaded = true)
 public class VirtualInterfaceApiLiveTest extends BaseNovaApiLiveTest {
-   private Optional<VirtualInterfaceApi> apiOption;
+   private Optional<? extends VirtualInterfaceApi> apiOption;
    private String zone;
 
 
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeApiLiveTest.java
index 94e5370..cbd1e52 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeApiLiveTest.java
@@ -48,7 +48,7 @@
 @Test(groups = "live", testName = "VolumeApiLiveTest", singleThreaded = true)
 public class VolumeApiLiveTest extends BaseNovaApiLiveTest {
 
-   private Optional<VolumeApi> volumeOption;
+   private Optional<? extends VolumeApi> volumeOption;
    private String zone;
 
    private Volume testVolume;
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeTypeApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeTypeApiLiveTest.java
index 65fbdca..9270a2e 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeTypeApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeTypeApiLiveTest.java
@@ -46,7 +46,7 @@
 @Test(groups = "live", testName = "VolumeTypeApiLiveTest", singleThreaded = true)
 public class VolumeTypeApiLiveTest extends BaseNovaApiLiveTest {
 
-   private Optional<VolumeTypeApi> volumeTypeOption;
+   private Optional<? extends VolumeTypeApi> volumeTypeOption;
    private String zone;
 
    private VolumeType testVolumeType;
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApiLiveTest.java
index 67217c5..066b4e4 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ExtensionApiLiveTest.java
@@ -23,8 +23,9 @@
 
 import java.util.Set;
 
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
 import org.jclouds.openstack.nova.v2_0.internal.BaseNovaApiLiveTest;
+import org.jclouds.openstack.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
 import org.testng.annotations.Test;
 
 /**
@@ -44,7 +45,7 @@
    public void testListExtensions() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
          ExtensionApi api = novaContext.getApi().getExtensionApiForZone(zoneId);
-         Set<Extension> response = api.listExtensions();
+         Set<? extends Extension> response = api.listExtensions();
          assert null != response;
          assertTrue(response.size() >= 0);
          for (Extension extension : response) {
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/FlavorApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/FlavorApiLiveTest.java
index 89d9c8e..87cc77c 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/FlavorApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/FlavorApiLiveTest.java
@@ -45,7 +45,7 @@
    public void testListFlavors() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
          FlavorApi api = novaContext.getApi().getFlavorApiForZone(zoneId);
-         Set<Resource> response = api.listFlavors();
+         Set<? extends Resource> response = api.listFlavors();
          assert null != response;
          assertTrue(response.size() >= 0);
          for (Resource flavor : response) {
@@ -66,7 +66,7 @@
    public void testListFlavorsInDetail() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
          FlavorApi api = novaContext.getApi().getFlavorApiForZone(zoneId);
-         Set<Flavor> response = api.listFlavorsInDetail();
+         Set<? extends Flavor> response = api.listFlavorsInDetail();
          assert null != response;
          assertTrue(response.size() >= 0);
          for (Flavor flavor : response) {
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ImageApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ImageApiLiveTest.java
index 792a6cc..0346665 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ImageApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ImageApiLiveTest.java
@@ -41,7 +41,7 @@
    public void testListImages() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
          ImageApi api = novaContext.getApi().getImageApiForZone(zoneId);
-         Set<Resource> response = api.listImages();
+         Set<? extends Resource> response = api.listImages();
          assertNotNull(response);
          assertTrue(response.size() >= 0);
          for (Resource image : response) {
@@ -58,7 +58,7 @@
    public void testListImagesInDetail() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
          ImageApi api = novaContext.getApi().getImageApiForZone(zoneId);
-         Set<Image> response = api.listImagesInDetail();
+         Set<? extends Image> response = api.listImagesInDetail();
          assertNotNull(response);
          assertTrue(response.size() >= 0);
          for (Image image : response) {
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
index 1a02473..f15604d 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
@@ -40,7 +40,7 @@
    public void testListServersInDetail() throws Exception {
       for (String zoneId : novaContext.getApi().getConfiguredZones()) {
          ServerApi api = novaContext.getApi().getServerApiForZone(zoneId);
-         Set<Resource> response = api.listServers();
+         Set<? extends Resource> response = api.listServers();
          assert null != response;
          assertTrue(response.size() >= 0);
          for (Resource server : response) {
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListNormalTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListNormalTest.java
index 8902516..6debbdf 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListNormalTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListNormalTest.java
@@ -28,7 +28,7 @@
 import org.jclouds.json.BaseSetParserTest;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.openstack.nova.v2_0.config.NovaParserModule;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.domain.Extension;
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListTest.java
index c6741a7..a19f6b6 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionListTest.java
@@ -28,7 +28,7 @@
 import org.jclouds.json.BaseSetParserTest;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.openstack.nova.v2_0.config.NovaParserModule;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.domain.Extension;
 import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Link.Relation;
 import org.jclouds.rest.annotations.SelectJson;
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionTest.java
index 4da904b..956c3ee 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseExtensionTest.java
@@ -27,7 +27,7 @@
 import org.jclouds.json.BaseItemParserTest;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.openstack.nova.v2_0.config.NovaParserModule;
-import org.jclouds.openstack.nova.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.domain.Extension;
 import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Link.Relation;
 import org.jclouds.rest.annotations.SelectJson;
diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java
index 15a6209..42947e9 100644
--- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java
+++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java
@@ -36,7 +36,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import javax.annotation.Nullable;
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.apis.BaseViewLiveTest;
@@ -48,6 +47,7 @@
 import org.jclouds.blobstore.domain.StorageType;
 import org.jclouds.domain.Location;
 import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
+import org.jclouds.javax.annotation.Nullable;
 import org.testng.ITestContext;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerLiveTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerLiveTest.java
index 61771be..4dcbc65 100644
--- a/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerLiveTest.java
+++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerLiveTest.java
@@ -27,12 +27,11 @@
 import java.util.List;
 import java.util.logging.Logger;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.blobstore.BlobStore;
 import org.jclouds.blobstore.domain.BlobMetadata;
 import org.jclouds.blobstore.domain.StorageMetadata;
 import org.jclouds.domain.Location;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.util.Strings2;
 import org.testng.SkipException;
 import org.testng.annotations.Test;
diff --git a/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java b/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
index 26c7743..e6943f4 100644
--- a/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
+++ b/compute/src/test/java/org/jclouds/compute/internal/BaseTemplateBuilderLiveTest.java
@@ -205,6 +205,22 @@
                   || location.getParent().getIso3166Codes().containsAll(location.getIso3166Codes()) : location + " ||"
                   + location.getParent();
             break;
+         case SYSTEM:
+            Location systemParent = location.getParent();
+            // loop up to root, which must be the provider
+            while (systemParent.getParent() != null) {
+                systemParent = systemParent.getParent();
+            }
+            assertProvider(systemParent);
+            break;
+         case NETWORK:
+             Location networkParent = location.getParent();
+             // loop up to root, which must be the provider
+             while (networkParent.getParent() != null) {
+                 networkParent = networkParent.getParent();
+             }
+             assertProvider(networkParent);
+             break;
          case HOST:
             Location provider2 = location.getParent().getParent().getParent();
             // zone can be a direct descendant of provider
diff --git a/core/pom.xml b/core/pom.xml
index 95eb57d..ac16508 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -106,7 +106,7 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>12.0</version>
+            <version>13.0</version>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
diff --git a/core/src/main/java/org/jclouds/location/predicates/LocationPredicates.java b/core/src/main/java/org/jclouds/location/predicates/LocationPredicates.java
index 3719cf3..f9e38c7 100644
--- a/core/src/main/java/org/jclouds/location/predicates/LocationPredicates.java
+++ b/core/src/main/java/org/jclouds/location/predicates/LocationPredicates.java
@@ -86,6 +86,40 @@
       }
    }
 
+   public static Predicate<Location> isSystem() {
+       return IsSystem.INSTANCE;
+    }
+
+    static enum IsSystem implements Predicate<Location> {
+       INSTANCE;
+       @Override
+       public boolean apply(Location input) {
+          return input.getScope() == LocationScope.SYSTEM;
+       }
+
+       @Override
+       public String toString() {
+          return "isSystem()";
+       }
+    }
+
+    public static Predicate<Location> isNetwork() {
+        return IsNetwork.INSTANCE;
+     }
+
+     static enum IsNetwork implements Predicate<Location> {
+        INSTANCE;
+        @Override
+        public boolean apply(Location input) {
+           return input.getScope() == LocationScope.NETWORK;
+        }
+
+        @Override
+        public String toString() {
+           return "isNetwork()";
+        }
+     }
+
    public static Predicate<Location> idEquals(String id) {
       return new IdEquals(id);
    }
diff --git a/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstNetwork.java b/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstNetwork.java
new file mode 100644
index 0000000..f78c944
--- /dev/null
+++ b/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstNetwork.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.location.suppliers.implicit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.transform;
+import static org.jclouds.location.predicates.LocationPredicates.isNetwork;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.domain.Location;
+import org.jclouds.location.functions.ToIdAndScope;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+
+import com.google.common.base.Supplier;
+
+/**
+ * 
+ * @author Dies Koper
+ * 
+ */
+@Singleton
+public class FirstNetwork implements ImplicitLocationSupplier {
+
+    private final Supplier<Set<? extends Location>> locationsSupplier;
+
+    @Inject
+    FirstNetwork(@Memoized Supplier<Set<? extends Location>> locationsSupplier) {
+        this.locationsSupplier = checkNotNull(locationsSupplier,
+                "locationsSupplierSupplier");
+    }
+
+    @Override
+    public Location get() {
+        Set<? extends Location> locations = locationsSupplier.get();
+        try {
+            return find(locations, isNetwork());
+        } catch (NoSuchElementException e) {
+            throw new NoSuchElementException(
+                    "none of the locations are scope NETWORK: "
+                            + transform(locations, ToIdAndScope.INSTANCE));
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/util/Optionals2.java b/core/src/main/java/org/jclouds/util/Optionals2.java
index 2ec4b1e..591c2df 100644
--- a/core/src/main/java/org/jclouds/util/Optionals2.java
+++ b/core/src/main/java/org/jclouds/util/Optionals2.java
@@ -20,6 +20,8 @@
 
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
 
 import com.google.common.base.Optional;
 
@@ -35,7 +37,11 @@
       if (optional) {
          ParameterizedType futureType = ParameterizedType.class.cast(method.getGenericReturnType());
          // TODO: error checking in case this is a type, not a class.
-         syncClass = Class.class.cast(futureType.getActualTypeArguments()[0]);
+         Type t = futureType.getActualTypeArguments()[0];
+         if (t instanceof WildcardType) {
+            t = ((WildcardType) t).getUpperBounds()[0];
+         }
+         syncClass = Class.class.cast(t);
       } else {
          syncClass = method.getReturnType();
       }
diff --git a/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstNetworkTest.java b/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstNetworkTest.java
new file mode 100644
index 0000000..520ade1
--- /dev/null
+++ b/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstNetworkTest.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.implicit;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code FirstNetwork}
+ * 
+ * @author Adrian Cole
+ * @author Dies Koper
+ */
+@Test(groups = "unit", testName = "FirstNetworkTest")
+public class FirstNetworkTest {
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("servo").description("http://servo")
+         .build();
+   Location region = new LocationBuilder().scope(LocationScope.REGION).id("servo-r").description("http://r.servo")
+         .parent(provider).build();
+   Location network = new LocationBuilder().scope(LocationScope.NETWORK).id("servo-n").description("http://z.r.servo")
+         .parent(region).build();
+
+   @Test
+   public void testDidntFindNetworkThrowsNSEEWithReasonableMessage() {
+      Supplier<Set<? extends Location>> supplier = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .<Location> of(provider, region));
+      FirstNetwork fn = new FirstNetwork(supplier);
+
+      try {
+         fn.get();
+         assert false;
+      } catch (NoSuchElementException e) {
+         assertEquals(e.getMessage(), "none of the locations are scope NETWORK: [servo:PROVIDER, servo-r:REGION]");
+      }
+   }
+
+   @Test
+   public void testFirstNetwork() {
+      Supplier<Set<? extends Location>> supplier = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .<Location> of(provider, region, network));
+      FirstNetwork fn = new FirstNetwork(supplier);
+      assertEquals(fn.get(), network);
+   }
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/ELBApi.java b/labs/elb/src/main/java/org/jclouds/elb/ELBApi.java
index 804eba5..5aee911 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/ELBApi.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/ELBApi.java
@@ -21,13 +21,12 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.concurrent.Timeout;
+import org.jclouds.elb.features.AvailabilityZoneApi;
 import org.jclouds.elb.features.InstanceApi;
 import org.jclouds.elb.features.LoadBalancerApi;
 import org.jclouds.elb.features.PolicyApi;
-import org.jclouds.elb.features.AvailabilityZoneApi;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
 import org.jclouds.rest.annotations.Delegate;
diff --git a/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncApi.java b/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncApi.java
index 9a910ab..009e3a8 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncApi.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncApi.java
@@ -20,13 +20,12 @@
 
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.aws.filters.FormSigner;
+import org.jclouds.elb.features.AvailabilityZoneAsyncApi;
 import org.jclouds.elb.features.InstanceAsyncApi;
 import org.jclouds.elb.features.LoadBalancerAsyncApi;
 import org.jclouds.elb.features.PolicyAsyncApi;
-import org.jclouds.elb.features.AvailabilityZoneAsyncApi;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
 import org.jclouds.rest.annotations.Delegate;
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/PolicyType.java b/labs/elb/src/main/java/org/jclouds/elb/domain/PolicyType.java
index a56ed82..49c21d0 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/domain/PolicyType.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/PolicyType.java
@@ -23,8 +23,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
@@ -135,7 +133,7 @@
       return Maps.uniqueIndex(attributeMetadata, new Function<AttributeMetadata<?>, String>(){
 
          @Override
-         public String apply(@Nullable AttributeMetadata<?> input) {
+         public String apply(AttributeMetadata<?> input) {
             return input.getName();
          }
          
diff --git a/labs/elb/src/test/java/org/jclouds/elb/features/PolicyApiLiveTest.java b/labs/elb/src/test/java/org/jclouds/elb/features/PolicyApiLiveTest.java
index 50c5cd9..b6155a1 100644
--- a/labs/elb/src/test/java/org/jclouds/elb/features/PolicyApiLiveTest.java
+++ b/labs/elb/src/test/java/org/jclouds/elb/features/PolicyApiLiveTest.java
@@ -22,8 +22,6 @@
 
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.elb.domain.AttributeMetadata;
 import org.jclouds.elb.domain.Policy;
 import org.jclouds.elb.domain.PolicyType;
@@ -95,7 +93,7 @@
          Iterable<String> names = Iterables.transform(response, new Function<PolicyType, String>() {
 
             @Override
-            public String apply(@Nullable PolicyType input) {
+            public String apply(PolicyType input) {
                return input.getName();
             }
 
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceContextModule.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceContextModule.java
index 2adb63c..98fe915 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceContextModule.java
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceContextModule.java
@@ -19,8 +19,6 @@
 
 package org.jclouds.nodepool.config;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule;
@@ -30,6 +28,7 @@
 import org.jclouds.compute.domain.Template;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LoginCredentials;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.nodepool.NodePoolApiMetadata;
 import org.jclouds.nodepool.NodePoolComputeServiceAdapter;
 import org.jclouds.nodepool.NodePoolComputeServiceContext;
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApi.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApi.java
index b066d77..e1a4867 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApi.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApi.java
@@ -26,6 +26,7 @@
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpoint;
 import org.jclouds.openstack.glance.v1_0.features.ImageApi;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -50,6 +51,13 @@
    Set<String> getConfiguredRegions();
 
    /**
+    * Provides synchronous access to Extension features.
+    */
+   @Delegate
+   ExtensionApi getExtensionApiForRegion(
+         @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region);
+
+   /**
     * Provides synchronous access to Image features.
     */
    @Delegate
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceAsyncApi.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceAsyncApi.java
index c67d9fd..d494d7a 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceAsyncApi.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceAsyncApi.java
@@ -24,6 +24,7 @@
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpoint;
 import org.jclouds.openstack.glance.v1_0.features.ImageAsyncApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -47,6 +48,13 @@
    Set<String> getConfiguredRegions();
 
    /**
+    * Provides asynchronous access to Extension features.
+    */
+   @Delegate
+   ExtensionAsyncApi getExtensionApiForRegion(
+         @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region);
+
+   /**
     * Provides asynchronous access to Image features.
     */
    @Delegate
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java
index c960417..e6d266a 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java
@@ -18,7 +18,13 @@
  */
 package org.jclouds.openstack.glance.v1_0.config;
 
+import java.net.URI;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Provider;
+import javax.inject.Singleton;
 
 import org.jclouds.http.HttpErrorHandler;
 import org.jclouds.http.annotation.ClientError;
@@ -26,15 +32,27 @@
 import org.jclouds.http.annotation.ServerError;
 import org.jclouds.json.config.GsonModule.DateAdapter;
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
-import org.jclouds.openstack.glance.v1_0.GlanceAsyncApi;
 import org.jclouds.openstack.glance.v1_0.GlanceApi;
-import org.jclouds.openstack.glance.v1_0.features.ImageAsyncApi;
+import org.jclouds.openstack.glance.v1_0.GlanceAsyncApi;
 import org.jclouds.openstack.glance.v1_0.features.ImageApi;
+import org.jclouds.openstack.glance.v1_0.features.ImageAsyncApi;
 import org.jclouds.openstack.glance.v1_0.handlers.GlanceErrorHandler;
+import org.jclouds.openstack.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
+import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.config.RestClientModule;
+import org.jclouds.rest.functions.ImplicitOptionalConverter;
 
+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.Multimap;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Provides;
 
 /**
  * Configures the Glance connection.
@@ -42,22 +60,47 @@
  * @author Adrian Cole
  */
 @ConfiguresRestClient
-public class GlanceRestClientModule extends RestClientModule<GlanceApi, GlanceAsyncApi> {
+public class GlanceRestClientModule<S extends GlanceApi, A extends GlanceAsyncApi> extends RestClientModule<S, A> {
 
    public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
+         .put(ExtensionApi.class, ExtensionAsyncApi.class)
          .put(ImageApi.class, ImageAsyncApi.class)
          .build();
 
    public GlanceRestClientModule() {
-      super(DELEGATE_MAP);
+      super(TypeToken.class.cast(TypeToken.of(GlanceApi.class)), TypeToken.class.cast(TypeToken.of(GlanceAsyncApi.class)), DELEGATE_MAP);
+   }
+
+   protected GlanceRestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType, Map<Class<?>, Class<?>> sync2Async) {
+      super(syncClientType, asyncClientType, sync2Async);
    }
 
    @Override
    protected void configure() {
       bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+      bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class);
       super.configure();
    }
    
+   @Provides
+   @Singleton
+   public Multimap<URI, URI> aliases() {
+       return ImmutableMultimap.<URI, URI>builder()
+          .build();
+   }
+
+   @Provides
+   @Singleton
+   public LoadingCache<String, Set<? extends Extension>> provideExtensionsByZone(final Provider<GlanceApi> glanceApi) {
+      return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS)
+            .build(new CacheLoader<String, Set<? extends Extension>>() {
+               @Override
+               public Set<? extends Extension> load(String key) throws Exception {
+                  return glanceApi.get().getExtensionApiForRegion(key).listExtensions();
+               }
+            });
+   }
+   
    @Override
    protected void bindErrorHandlers() {
       bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(GlanceErrorHandler.class);
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/features/ImageApi.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/features/ImageApi.java
index c86757a..b24e368 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/features/ImageApi.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/features/ImageApi.java
@@ -44,12 +44,12 @@
    /**
     * Returns a set of brief metadata about images
     */
-   Set<Image> list(ListImageOptions... options);
+   Set<? extends Image> list(ListImageOptions... options);
 
    /**
     * Returns a set of detailed metadata about images
     */
-   Set<ImageDetails> listInDetail(ListImageOptions... options);
+   Set<? extends ImageDetails> listInDetail(ListImageOptions... options);
 
    /**
     * Return metadata about an image with id
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/features/ImageAsyncApi.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/features/ImageAsyncApi.java
index b7c467a..c4b41ca 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/features/ImageAsyncApi.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/features/ImageAsyncApi.java
@@ -73,7 +73,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/images")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Image>> list(ListImageOptions... options);
+   ListenableFuture<? extends Set<? extends Image>> list(ListImageOptions... options);
    
    /**
     * @see ImageApi#listInDetail
@@ -83,7 +83,7 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/images/detail")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<ImageDetails>> listInDetail(ListImageOptions... options);
+   ListenableFuture<? extends Set<? extends ImageDetails>> listInDetail(ListImageOptions... options);
    
    /**
     * @see ImageApi#show
@@ -110,7 +110,7 @@
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    @SelectJson("image")
    @Consumes(MediaType.APPLICATION_JSON)
-   ListenableFuture<ImageDetails> create(@HeaderParam("x-image-meta-name") String name, Payload payload, CreateImageOptions... options);
+   ListenableFuture<? extends ImageDetails> create(@HeaderParam("x-image-meta-name") String name, Payload payload, CreateImageOptions... options);
 
    /**
     * @see ImageApi#reserve
@@ -119,7 +119,7 @@
    @Path("/images")
    @SelectJson("image")
    @Consumes(MediaType.APPLICATION_JSON)
-   ListenableFuture<ImageDetails> reserve(@HeaderParam("x-image-meta-name") String name, CreateImageOptions... options);
+   ListenableFuture<? extends ImageDetails> reserve(@HeaderParam("x-image-meta-name") String name, CreateImageOptions... options);
 
    /**
     * @see ImageApi#upload
@@ -129,7 +129,7 @@
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    @SelectJson("image")
    @Consumes(MediaType.APPLICATION_JSON)
-   ListenableFuture<ImageDetails> upload(@PathParam("id") String id, Payload imageData, UpdateImageOptions... options);
+   ListenableFuture<? extends ImageDetails> upload(@PathParam("id") String id, Payload imageData, UpdateImageOptions... options);
 
    /**
     * @see ImageApi#update
@@ -138,7 +138,7 @@
    @Path("/images/{id}")
    @SelectJson("image")
    @Consumes(MediaType.APPLICATION_JSON)
-   ListenableFuture<ImageDetails> update(@PathParam("id") String id, UpdateImageOptions... options);
+   ListenableFuture<? extends ImageDetails> update(@PathParam("id") String id, UpdateImageOptions... options);
 
    /**
     * @see ImageApi#delete
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/functions/ParseImageDetailsFromHeaders.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/functions/ParseImageDetailsFromHeaders.java
index 44ecc33..9bcb4e4 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/functions/ParseImageDetailsFromHeaders.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/functions/ParseImageDetailsFromHeaders.java
@@ -18,7 +18,21 @@
  */
 package org.jclouds.openstack.glance.v1_0.functions;
 
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.*;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.CHECKSUM;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.CONTAINER_FORMAT;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.CREATED_AT;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.DELETED_AT;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.DISK_FORMAT;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.ID;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.IS_PUBLIC;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.LOCATION;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.MIN_DISK;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.MIN_RAM;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.NAME;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.OWNER;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.SIZE;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.STATUS;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.UPDATED_AT;
 
 import javax.inject.Inject;
 
@@ -44,6 +58,7 @@
       this.dateService = dateService;
    }
 
+   @Override
    public ImageDetails apply(HttpResponse from) {
       ImageDetails.Builder<?> builder = ImageDetails.builder()
                 .id(from.getFirstHeaderOrNull(ID.asHeader()))
diff --git a/labs/openstack-glance/src/test/java/org/jclouds/openstack/glance/v1_0/features/ImageApiLiveTest.java b/labs/openstack-glance/src/test/java/org/jclouds/openstack/glance/v1_0/features/ImageApiLiveTest.java
index 6be9b4a..ef45754 100644
--- a/labs/openstack-glance/src/test/java/org/jclouds/openstack/glance/v1_0/features/ImageApiLiveTest.java
+++ b/labs/openstack-glance/src/test/java/org/jclouds/openstack/glance/v1_0/features/ImageApiLiveTest.java
@@ -49,7 +49,7 @@
    public void testList() throws Exception {
       for (String zoneId : glanceContext.getApi().getConfiguredRegions()) {
          ImageApi api = glanceContext.getApi().getImageApiForRegion(zoneId);
-         Set<Image> response = api.list(ListImageOptions.Builder.limit(100));
+         Set<? extends Image> response = api.list(ListImageOptions.Builder.limit(100));
          assert null != response;
          for (Image image : response) {
             checkImage(image);
@@ -67,7 +67,7 @@
    public void testListInDetail() throws Exception {
       for (String zoneId : glanceContext.getApi().getConfiguredRegions()) {
          ImageApi api = glanceContext.getApi().getImageApiForRegion(zoneId);
-         Set<ImageDetails> response = api.listInDetail();
+         Set<? extends ImageDetails> response = api.listInDetail();
          assert null != response;
          for (ImageDetails image : response) {
             checkImage(image);
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApi.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApi.java
index 92f2a67..2f9a5ed 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApi.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApi.java
@@ -28,8 +28,10 @@
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpoint;
+import org.jclouds.location.functions.ZoneToEndpoint;
 import org.jclouds.openstack.quantum.v1_0.features.NetworkApi;
 import org.jclouds.openstack.quantum.v1_0.features.PortApi;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -53,6 +55,13 @@
    Set<String> getConfiguredRegions();
 
    /**
+    * Provides synchronous access to Extension features.
+    */
+   @Delegate
+   ExtensionApi getExtensionApiForRegion(
+         @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region);
+
+   /**
     * Provides synchronous access to Network features.
     */
    @Delegate
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumAsyncApi.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumAsyncApi.java
index 70f1d19..0ea08a3 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumAsyncApi.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumAsyncApi.java
@@ -28,6 +28,7 @@
 import org.jclouds.location.functions.RegionToEndpoint;
 import org.jclouds.openstack.quantum.v1_0.features.NetworkAsyncApi;
 import org.jclouds.openstack.quantum.v1_0.features.PortAsyncApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -51,6 +52,13 @@
    Set<String> getConfiguredRegions();
 
    /**
+    * Provides asynchronous access to Extension features.
+    */
+   @Delegate
+   ExtensionAsyncApi getExtensionApiForRegion(
+         @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region);
+
+   /**
     * Provides asynchronous access to Network features.
     */
    @Delegate
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java
index 7b659b1..4b7be5b 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java
@@ -18,7 +18,13 @@
  */
 package org.jclouds.openstack.quantum.v1_0.config;
 
+import java.net.URI;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Provider;
+import javax.inject.Singleton;
 
 import org.jclouds.http.HttpErrorHandler;
 import org.jclouds.http.annotation.ClientError;
@@ -26,17 +32,29 @@
 import org.jclouds.http.annotation.ServerError;
 import org.jclouds.json.config.GsonModule.DateAdapter;
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
-import org.jclouds.openstack.quantum.v1_0.QuantumAsyncApi;
 import org.jclouds.openstack.quantum.v1_0.QuantumApi;
-import org.jclouds.openstack.quantum.v1_0.features.NetworkAsyncApi;
+import org.jclouds.openstack.quantum.v1_0.QuantumAsyncApi;
 import org.jclouds.openstack.quantum.v1_0.features.NetworkApi;
-import org.jclouds.openstack.quantum.v1_0.features.PortAsyncApi;
+import org.jclouds.openstack.quantum.v1_0.features.NetworkAsyncApi;
 import org.jclouds.openstack.quantum.v1_0.features.PortApi;
+import org.jclouds.openstack.quantum.v1_0.features.PortAsyncApi;
 import org.jclouds.openstack.quantum.v1_0.handlers.QuantumErrorHandler;
+import org.jclouds.openstack.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
+import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.config.RestClientModule;
+import org.jclouds.rest.functions.ImplicitOptionalConverter;
 
+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.Multimap;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Provides;
 
 /**
  * Configures the Quantum connection.
@@ -44,23 +62,48 @@
  * @author Adam Lowe
  */
 @ConfiguresRestClient
-public class QuantumRestClientModule extends RestClientModule<QuantumApi, QuantumAsyncApi> {
+public class QuantumRestClientModule<S extends QuantumApi, A extends QuantumAsyncApi> extends RestClientModule<S, A> {
 
    public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
+         .put(ExtensionApi.class, ExtensionAsyncApi.class)
          .put(NetworkApi.class, NetworkAsyncApi.class)
          .put(PortApi.class, PortAsyncApi.class)
          .build();
 
    public QuantumRestClientModule() {
-      super(DELEGATE_MAP);
+      super(TypeToken.class.cast(TypeToken.of(QuantumApi.class)), TypeToken.class.cast(TypeToken.of(QuantumAsyncApi.class)), DELEGATE_MAP);
+   }
+
+   protected QuantumRestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType, Map<Class<?>, Class<?>> sync2Async) {
+      super(syncClientType, asyncClientType, sync2Async);
    }
    
    @Override
    protected void configure() {
       bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+      bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class);
       super.configure();
    }
    
+   @Provides
+   @Singleton
+   public Multimap<URI, URI> aliases() {
+       return ImmutableMultimap.<URI, URI>builder()
+          .build();
+   }
+
+   @Provides
+   @Singleton
+   public LoadingCache<String, Set<? extends Extension>> provideExtensionsByZone(final Provider<QuantumApi> quantumApi) {
+      return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS)
+            .build(new CacheLoader<String, Set<? extends Extension>>() {
+               @Override
+               public Set<? extends Extension> load(String key) throws Exception {
+                  return quantumApi.get().getExtensionApiForRegion(key).listExtensions();
+               }
+            });
+   }
+   
    @Override
    protected void bindErrorHandlers() {
       bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(QuantumErrorHandler.class);
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApi.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApi.java
index bd970fc..116a23e 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApi.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApi.java
@@ -44,12 +44,12 @@
     * Returns the list of all networks currently defined in Quantum for the current tenant. The list provides the unique
     * identifier of each network configured for the tenant.
     */
-   Set<Reference> listReferences();
+   Set<? extends Reference> listReferences();
 
    /**
     * Returns all networks currently defined in Quantum for the current tenant.
     */
-   Set<Network> list();
+   Set<? extends Network> list();
 
    /**
     * Returns the specific network.
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/NetworkAsyncApi.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/NetworkAsyncApi.java
index 44a8fea..ffb6805 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/NetworkAsyncApi.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/NetworkAsyncApi.java
@@ -64,7 +64,7 @@
    @GET
    @SelectJson("networks")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Reference>> listReferences();
+   ListenableFuture<? extends Set<? extends Reference>> listReferences();
 
    /**
     * @see NetworkApi#list
@@ -73,7 +73,7 @@
    @SelectJson("networks")
    @Path("/detail")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Network>> list();
+   ListenableFuture<? extends Set<? extends Network>> list();
 
    /**
     * @see NetworkApi#get
@@ -82,7 +82,7 @@
    @SelectJson("network")
    @Path("/{id}")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Network> get(@PathParam("id") String id);
+   ListenableFuture<? extends Network> get(@PathParam("id") String id);
 
    /**
     * @see NetworkApi#getDetails
@@ -91,7 +91,7 @@
    @SelectJson("network")
    @Path("/{id}/detail")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<NetworkDetails> getDetails(@PathParam("id") String id);
+   ListenableFuture<? extends NetworkDetails> getDetails(@PathParam("id") String id);
 
    /**
     * @see NetworkApi#create
@@ -100,7 +100,7 @@
    @SelectJson("network")
    @Produces(MediaType.APPLICATION_JSON)
    @WrapWith("network")
-   ListenableFuture<Reference> create(@PayloadParam("name") String name);
+   ListenableFuture<? extends Reference> create(@PayloadParam("name") String name);
 
    /**
     * @see NetworkApi#rename
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/PortApi.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/PortApi.java
index 5acc661..5f65506 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/PortApi.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/PortApi.java
@@ -44,12 +44,12 @@
    /**
     * Returns the list of all ports currently defined in Quantum for the requested network
     */
-   Set<Reference> listReferences();
+   Set<? extends Reference> listReferences();
 
    /**
     * Returns the set of ports currently defined in Quantum for the requested network.
     */
-   Set<Port> list();
+   Set<? extends Port> list();
 
    /**
     * Returns a specific port.
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/PortAsyncApi.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/PortAsyncApi.java
index c0c1e44..a11b474 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/PortAsyncApi.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/features/PortAsyncApi.java
@@ -64,7 +64,7 @@
    @GET
    @SelectJson("ports")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Reference>> listReferences();
+   ListenableFuture<? extends Set<? extends Reference>> listReferences();
 
    /**
     * @see PortApi#list
@@ -73,7 +73,7 @@
    @SelectJson("ports")
    @Path("/detail")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<Port>> list();
+   ListenableFuture<? extends Set<? extends Port>> list();
 
    /**
     * @see PortApi#get
@@ -82,7 +82,7 @@
    @SelectJson("port")
    @Path("/{id}")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<Port> get(@PathParam("id") String id);
+   ListenableFuture<? extends Port> get(@PathParam("id") String id);
 
    /**
     * @see PortApi#getDetails
@@ -92,14 +92,14 @@
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/{id}/detail")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<PortDetails> getDetails(@PathParam("id") String id);
+   ListenableFuture<? extends PortDetails> getDetails(@PathParam("id") String id);
 
    /**
     * @see PortApi#create()
     */
    @POST
    @SelectJson("port")
-   ListenableFuture<Reference> create();
+   ListenableFuture<? extends Reference> create();
 
    /**
     * @see PortApi#create(org.jclouds.openstack.quantum.v1_0.domain.Port.State) 
@@ -131,7 +131,7 @@
    @SelectJson("attachment")
    @Path("/{id}/attachment")
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)   
-   ListenableFuture<Attachment> showAttachment(@PathParam("id") String portId);
+   ListenableFuture<? extends Attachment> showAttachment(@PathParam("id") String portId);
 
    /**
     * @see PortApi#plugAttachment
diff --git a/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApiExpectTest.java b/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApiExpectTest.java
index da8b087..bf23dfb 100644
--- a/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApiExpectTest.java
+++ b/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApiExpectTest.java
@@ -55,7 +55,7 @@
             HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/list_network_refs.json", APPLICATION_JSON)).build())
             .getNetworkApiForRegion("region-a.geo-1");
       
-      Set<Reference> nets = api.listReferences();
+      Set<? extends Reference> nets = api.listReferences();
       assertEquals(nets, listOfNetworkRefs());
    }
 
@@ -76,7 +76,7 @@
             HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/list_networks.json", APPLICATION_JSON)).build())
             .getNetworkApiForRegion("region-a.geo-1");
 
-      Set<Network> nets = api.list();
+      Set<? extends Network> nets = api.list();
       assertEquals(nets, listOfNetworks());
    }
 
diff --git a/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApiLiveTest.java b/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApiLiveTest.java
index a0060be..5add5bf 100644
--- a/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApiLiveTest.java
+++ b/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/NetworkApiLiveTest.java
@@ -42,8 +42,8 @@
 
    public void testListNetworks() {
       for (String regionId : quantumContext.getApi().getConfiguredRegions()) {
-         Set<Reference> ids = quantumContext.getApi().getNetworkApiForRegion(regionId).listReferences();
-         Set<Network> networks = quantumContext.getApi().getNetworkApiForRegion(regionId).list();
+         Set<? extends Reference> ids = quantumContext.getApi().getNetworkApiForRegion(regionId).listReferences();
+         Set<? extends Network> networks = quantumContext.getApi().getNetworkApiForRegion(regionId).list();
          assertNotNull(ids);
          assertEquals(ids.size(), networks.size());
          for (Network network : networks) {
diff --git a/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/PortApiExpectTest.java b/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/PortApiExpectTest.java
index 640b6ed..02a61a1 100644
--- a/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/PortApiExpectTest.java
+++ b/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/PortApiExpectTest.java
@@ -56,7 +56,7 @@
             HttpResponse.builder().statusCode(200).payload(payloadFromStringWithContentType("{\"ports\": [{\"id\": \"a6058a59-fa8c-46cc-bac8-08904e6ff0a5\"}]}", APPLICATION_JSON)).build())
             .getPortApiForRegionAndNetwork("region-a.geo-1", "1a104cf5-cb18-4d35-9407-2fd2646d9d0b");
 
-      Set<Reference> nets = api.listReferences();
+      Set<? extends Reference> nets = api.listReferences();
       assertEquals(nets, ImmutableSet.of(Reference.builder().id("a6058a59-fa8c-46cc-bac8-08904e6ff0a5").build()));
    }
 
@@ -77,7 +77,7 @@
             HttpResponse.builder().statusCode(200).payload(payloadFromStringWithContentType("{\"ports\": [{\"state\": \"DOWN\", \"id\": \"814ae4bb-33d9-425f-8ee2-13a5c90b1465\"}]}", APPLICATION_JSON)).build())
             .getPortApiForRegionAndNetwork("region-a.geo-1", "1a104cf5-cb18-4d35-9407-2fd2646d9d0b");
 
-      Set<Port> nets = api.list();
+      Set<? extends Port> nets = api.list();
       assertEquals(nets, ImmutableSet.of(Port.builder().state(Port.State.DOWN).id("814ae4bb-33d9-425f-8ee2-13a5c90b1465").build()));
    }
 
diff --git a/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/PortApiLiveTest.java b/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/PortApiLiveTest.java
index 68c92a6..b179061 100644
--- a/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/PortApiLiveTest.java
+++ b/labs/openstack-quantum/src/test/java/org/jclouds/openstack/quantum/v1_0/features/PortApiLiveTest.java
@@ -46,11 +46,11 @@
    public void testListPorts() {
       for (String regionId : quantumContext.getApi().getConfiguredRegions()) {
          NetworkApi netApi = quantumContext.getApi().getNetworkApiForRegion(regionId);
-         Set<Reference> nets = netApi.listReferences();
+         Set<? extends Reference> nets = netApi.listReferences();
          for(Reference net : nets) {
             PortApi portApi = quantumContext.getApi().getPortApiForRegionAndNetwork(regionId, net.getId());
-            Set<Reference> portRefs = portApi.listReferences();
-            Set<Port> ports = portApi.list();
+            Set<? extends Reference> portRefs = portApi.listReferences();
+            Set<? extends Port> ports = portApi.list();
             
             assertEquals(portRefs.size(), ports.size());
             for (Port port : ports) {
diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java
index f9e3fc5..3fdbe83 100644
--- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java
+++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java
@@ -28,6 +28,7 @@
 import org.jclouds.openstack.swift.v1.features.AccountApi;
 import org.jclouds.openstack.swift.v1.features.ContainerApi;
 import org.jclouds.openstack.swift.v1.features.ObjectApi;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -52,6 +53,13 @@
    Set<String> getConfiguredRegions();
 
    /**
+    * Provides synchronous access to Extension features.
+    */
+   @Delegate
+   ExtensionApi getExtensionApiForRegion(
+         @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region);
+
+   /**
     * Provides synchronous access to Account features.
     */
    @Delegate
diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftAsyncApi.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftAsyncApi.java
index f74ad26..4f11970 100644
--- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftAsyncApi.java
+++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftAsyncApi.java
@@ -26,6 +26,7 @@
 import org.jclouds.openstack.swift.v1.features.AccountAsyncApi;
 import org.jclouds.openstack.swift.v1.features.ContainerAsyncApi;
 import org.jclouds.openstack.swift.v1.features.ObjectAsyncApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -49,6 +50,13 @@
    Set<String> getConfiguredRegions();
 
    /**
+    * Provides asynchronous access to Extension features.
+    */
+   @Delegate
+   ExtensionAsyncApi getExtensionApiForRegion(
+         @EndpointParam(parser = RegionToEndpoint.class) @Nullable String region);
+
+   /**
     * Provides asynchronous access to Account features.
     */
    @Delegate
diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftRestClientModule.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftRestClientModule.java
index 5089f03..a0489d0 100644
--- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftRestClientModule.java
+++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftRestClientModule.java
@@ -18,7 +18,13 @@
  */
 package org.jclouds.openstack.swift.v1.config;
 
+import java.net.URI;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Provider;
+import javax.inject.Singleton;
 
 import org.jclouds.http.HttpErrorHandler;
 import org.jclouds.http.annotation.ClientError;
@@ -26,19 +32,31 @@
 import org.jclouds.http.annotation.ServerError;
 import org.jclouds.json.config.GsonModule.DateAdapter;
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
-import org.jclouds.openstack.swift.v1.SwiftAsyncApi;
 import org.jclouds.openstack.swift.v1.SwiftApi;
-import org.jclouds.openstack.swift.v1.features.AccountAsyncApi;
+import org.jclouds.openstack.swift.v1.SwiftAsyncApi;
 import org.jclouds.openstack.swift.v1.features.AccountApi;
-import org.jclouds.openstack.swift.v1.features.ContainerAsyncApi;
+import org.jclouds.openstack.swift.v1.features.AccountAsyncApi;
 import org.jclouds.openstack.swift.v1.features.ContainerApi;
-import org.jclouds.openstack.swift.v1.features.ObjectAsyncApi;
+import org.jclouds.openstack.swift.v1.features.ContainerAsyncApi;
 import org.jclouds.openstack.swift.v1.features.ObjectApi;
+import org.jclouds.openstack.swift.v1.features.ObjectAsyncApi;
 import org.jclouds.openstack.swift.v1.handlers.SwiftErrorHandler;
+import org.jclouds.openstack.v2_0.domain.Extension;
+import org.jclouds.openstack.v2_0.features.ExtensionApi;
+import org.jclouds.openstack.v2_0.features.ExtensionAsyncApi;
+import org.jclouds.openstack.v2_0.functions.PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.config.RestClientModule;
+import org.jclouds.rest.functions.ImplicitOptionalConverter;
 
+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.Multimap;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Provides;
 
 /**
  * Configures the Swift connection.
@@ -46,23 +64,48 @@
  * @author Adrian Cole
  */
 @ConfiguresRestClient
-public class SwiftRestClientModule extends RestClientModule<SwiftApi, SwiftAsyncApi> {
+public class SwiftRestClientModule<S extends SwiftApi, A extends SwiftAsyncApi> extends RestClientModule<S, A> {
 
    public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
+         .put(ExtensionApi.class, ExtensionAsyncApi.class)
          .put(AccountApi.class, AccountAsyncApi.class)
          .put(ContainerApi.class, ContainerAsyncApi.class)
          .put(ObjectApi.class, ObjectAsyncApi.class)
          .build();
 
    public SwiftRestClientModule() {
-      super(DELEGATE_MAP);
+      super(TypeToken.class.cast(TypeToken.of(SwiftApi.class)), TypeToken.class.cast(TypeToken.of(SwiftAsyncApi.class)), DELEGATE_MAP);
+   }
+
+   protected SwiftRestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType, Map<Class<?>, Class<?>> sync2Async) {
+      super(syncClientType, asyncClientType, sync2Async);
    }
    
    @Override
    protected void configure() {
       bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+      bind(ImplicitOptionalConverter.class).to(PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensionsSet.class);
       super.configure();
    }
+   
+   @Provides
+   @Singleton
+   public Multimap<URI, URI> aliases() {
+       return ImmutableMultimap.<URI, URI>builder()
+          .build();
+   }
+
+   @Provides
+   @Singleton
+   public LoadingCache<String, Set<? extends Extension>> provideExtensionsByZone(final Provider<SwiftApi> swiftApi) {
+      return CacheBuilder.newBuilder().expireAfterWrite(23, TimeUnit.HOURS)
+            .build(new CacheLoader<String, Set<? extends Extension>>() {
+               @Override
+               public Set<? extends Extension> load(String key) throws Exception {
+                  return swiftApi.get().getExtensionApiForRegion(key).listExtensions();
+               }
+            });
+   }
 
    @Override
    protected void bindErrorHandlers() {
diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java
index 8bdbcec..1cf2e2b 100644
--- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java
+++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java
@@ -47,7 +47,7 @@
    /**
     * @see #listContainers(ListContainersOptions)
     */
-   Set<ContainerMetadata> listContainers();
+   Set<? extends ContainerMetadata> listContainers();
 
    /**
     * retrieve a list of existing storage containers ordered by name. The sort order for the name is
@@ -57,6 +57,6 @@
     * @param options
     * @return a list of existing storage containers ordered by name.
     */
-   Set<ContainerMetadata> listContainers(ListContainersOptions options);
+   Set<? extends ContainerMetadata> listContainers(ListContainersOptions options);
 
 }
diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountAsyncApi.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountAsyncApi.java
index 542fff1..de64dee 100644
--- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountAsyncApi.java
+++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountAsyncApi.java
@@ -59,7 +59,7 @@
    @HEAD
    @ResponseParser(ParseAccountMetadataResponseFromHeaders.class)
    @Path("/")
-   ListenableFuture<AccountMetadata> getAccountMetadata();
+   ListenableFuture<? extends AccountMetadata> getAccountMetadata();
 
    /**
     * @see AccountApi#listContainers()
@@ -69,7 +69,7 @@
    @QueryParams(keys = "format", values = "json")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
    @Path("/")
-   ListenableFuture<Set<ContainerMetadata>> listContainers();
+   ListenableFuture<? extends Set<? extends ContainerMetadata>> listContainers();
 
    /**
     * @see AccountApi#listContainers(ListContainersOptions)
@@ -79,5 +79,5 @@
    @QueryParams(keys = "format", values = "json")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
    @Path("/")
-   ListenableFuture<Set<ContainerMetadata>> listContainers(ListContainersOptions options);
+   ListenableFuture<? extends Set<? extends ContainerMetadata>> listContainers(ListContainersOptions options);
 }
diff --git a/labs/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiLiveTest.java b/labs/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiLiveTest.java
index c3633dd..8521c0e 100644
--- a/labs/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiLiveTest.java
+++ b/labs/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiLiveTest.java
@@ -49,7 +49,7 @@
    public void testListContainers() throws Exception {
       for (String regionId : swiftContext.getApi().getConfiguredRegions()) {
          AccountApi api = swiftContext.getApi().getAccountApiForRegion(regionId);
-         Set<ContainerMetadata> response = api.listContainers();
+         Set<? extends ContainerMetadata> response = api.listContainers();
          assertNotNull(response);
          for (ContainerMetadata container : response) {
             assertNotNull(container.getName());
diff --git a/labs/rds/src/main/java/org/jclouds/rds/RDSApi.java b/labs/rds/src/main/java/org/jclouds/rds/RDSApi.java
index 1e548b9..37f1ff4 100644
--- a/labs/rds/src/main/java/org/jclouds/rds/RDSApi.java
+++ b/labs/rds/src/main/java/org/jclouds/rds/RDSApi.java
@@ -21,9 +21,8 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.concurrent.Timeout;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
 import org.jclouds.rds.features.InstanceApi;
diff --git a/labs/rds/src/main/java/org/jclouds/rds/RDSAsyncApi.java b/labs/rds/src/main/java/org/jclouds/rds/RDSAsyncApi.java
index b61cb91..12cd599 100644
--- a/labs/rds/src/main/java/org/jclouds/rds/RDSAsyncApi.java
+++ b/labs/rds/src/main/java/org/jclouds/rds/RDSAsyncApi.java
@@ -20,9 +20,8 @@
 
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.aws.filters.FormSigner;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
 import org.jclouds.rds.features.InstanceAsyncApi;
diff --git a/labs/rds/src/test/java/org/jclouds/rds/features/InstanceApiLiveTest.java b/labs/rds/src/test/java/org/jclouds/rds/features/InstanceApiLiveTest.java
index d88a34b..003fa81 100644
--- a/labs/rds/src/test/java/org/jclouds/rds/features/InstanceApiLiveTest.java
+++ b/labs/rds/src/test/java/org/jclouds/rds/features/InstanceApiLiveTest.java
@@ -26,8 +26,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.collect.IterableWithMarker;
 import org.jclouds.predicates.InetSocketAddressConnect;
 import org.jclouds.predicates.RetryablePredicate;
@@ -95,7 +93,7 @@
                      return Iterables.all(sgApi().get(input.getName()).getIPRanges(), new Predicate<Authorization>() {
 
                         @Override
-                        public boolean apply(@Nullable Authorization i2) {
+                        public boolean apply(Authorization i2) {
                            return i2.getStatus() == Status.AUTHORIZED;
                         }
 
diff --git a/labs/rds/src/test/java/org/jclouds/rds/features/SecurityGroupApiLiveTest.java b/labs/rds/src/test/java/org/jclouds/rds/features/SecurityGroupApiLiveTest.java
index 0fc0697..d48534d 100644
--- a/labs/rds/src/test/java/org/jclouds/rds/features/SecurityGroupApiLiveTest.java
+++ b/labs/rds/src/test/java/org/jclouds/rds/features/SecurityGroupApiLiveTest.java
@@ -25,8 +25,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.collect.IterableWithMarker;
 import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.rds.domain.Authorization;
@@ -65,7 +63,7 @@
             return Iterables.all(api().get(input.getName()).getIPRanges(), new Predicate<Authorization>() {
 
                @Override
-               public boolean apply(@Nullable Authorization i2) {
+               public boolean apply(Authorization i2) {
                   return i2.getStatus() == Status.AUTHORIZED;
                }
 
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/SmartOSApiMetadata.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/SmartOSApiMetadata.java
index 8e29b1e..873be70 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/SmartOSApiMetadata.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/SmartOSApiMetadata.java
@@ -6,6 +6,10 @@
 import org.jclouds.apis.internal.BaseApiMetadata;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.smartos.compute.config.SmartOSComputeServiceContextModule;
+import org.jclouds.smartos.compute.config.SmartOSParserModule;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
 
 /**
  * Implementation of {@link ApiMetadata} for SmartOS
@@ -38,14 +42,16 @@
 
       protected Builder() {
          id("smartos-ssh")
-                  .name("SmartOS SSH API")
-                  .identityName("Username")
-                  .defaultIdentity("root")
-                  .defaultCredential("smartos")
-                  .defaultEndpoint("http://localhost")
-                  .documentation(
-                           URI.create("http://http://wiki.smartos.org/display/DOC/How+to+create+a+Virtual+Machine+in+SmartOS"))
-                  .view(ComputeServiceContext.class).defaultModule(SmartOSComputeServiceContextModule.class);
+         .name("SmartOS SSH API")
+         .identityName("Username")
+         .defaultIdentity("root")
+         .defaultCredential("smartos")
+         .defaultEndpoint("http://localhost")
+         .documentation(URI.create("http://http://wiki.smartos.org/display/DOC/How+to+create+a+Virtual+Machine+in+SmartOS"))
+         .view(ComputeServiceContext.class)
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(SmartOSComputeServiceContextModule.class)
+                                     .add(SmartOSParserModule.class).build());
       }
 
       @Override
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/SmartOSHost.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/SmartOSHostController.java
similarity index 73%
rename from labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/SmartOSHost.java
rename to labs/smartos-ssh/src/main/java/org/jclouds/smartos/SmartOSHostController.java
index 248f42f..30cff22 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/SmartOSHost.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/SmartOSHostController.java
@@ -1,4 +1,4 @@
-package org.jclouds.smartos.compute.domain;
+package org.jclouds.smartos;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -9,13 +9,17 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.jclouds.domain.LoginCredentials;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.Json;
 import org.jclouds.location.Provider;
 import org.jclouds.rest.annotations.Credential;
 import org.jclouds.rest.annotations.Identity;
+import org.jclouds.smartos.compute.domain.DataSet;
+import org.jclouds.smartos.compute.domain.VM;
+import org.jclouds.smartos.compute.domain.VmSpecification;
 import org.jclouds.ssh.SshClient;
 
 import com.google.common.base.Splitter;
@@ -23,18 +27,19 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.net.HostAndPort;
+import com.google.common.util.concurrent.RateLimiter;
 
 /**
  * A host machine that runs smartOS
  */
-public class SmartOSHost {
+public class SmartOSHostController {
    protected final String hostname;
    protected final String username;
    protected final String password;
+   protected final SshClient.Factory sshClientFactory;
+   protected final Json json;
 
-   protected SshClient.Factory sshClientFactory;
-
-   private SshClient _connection;
+   protected transient SshClient _connection;
 
    public static class HostException extends RuntimeException {
       private static final long serialVersionUID = -2247796213703641847L;
@@ -55,65 +60,14 @@
       }
    }
 
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public Builder toBuilder() {
-      return builder().fromSmartOSHost(this);
-   }
-
-   public static class Builder {
-      protected String hostname;
-      protected String username;
-      protected String password;
-      protected SshClient.Factory sshFactory;
-
-      public Builder hostname(String hostname) {
-         this.hostname = hostname;
-         return this;
-      }
-
-      public Builder username(String username) {
-         this.username = username;
-         return this;
-      }
-
-      public Builder password(String password) {
-         this.password = password;
-         return this;
-      }
-
-      public Builder sshFactory(SshClient.Factory sshFactory) {
-         this.sshFactory = sshFactory;
-         return this;
-      }
-
-      public SmartOSHost build() {
-         return new SmartOSHost(hostname, username, password, sshFactory);
-      }
-
-      public Builder fromSmartOSHost(SmartOSHost in) {
-         return this.hostname(in.getHostname()).username(in.getHostname()).password(in.getPassword())
-                  .sshFactory(in.getSshClientFactory());
-      }
-   }
-
    @Inject
-   protected SmartOSHost(@Provider Supplier<URI> provider, @Nullable @Identity String identity,
-            @Nullable @Credential String credential, SshClient.Factory sshFactory) {
-
+   protected SmartOSHostController(@Provider Supplier<URI> provider, @Nullable @Identity String identity,
+            @Nullable @Credential String credential, SshClient.Factory sshFactory, Json json) {
       this.hostname = provider.get().getHost();
       this.username = identity;
       this.password = credential;
       this.sshClientFactory = sshFactory;
-   }
-
-   protected SmartOSHost(String hostname, String username, String password, SshClient.Factory sshClientFactory) {
-      this.hostname = hostname;
-      this.username = username;
-      this.password = password;
-      this.sshClientFactory = sshClientFactory;
+      this.json = json;
    }
 
    public String getDescription() {
@@ -202,8 +156,8 @@
 
    public VM createVM(VmSpecification specification) {
 
-      String response = getConnection().exec(
-               "(cat <<END\n" + specification.toJSONSpecification() + "\nEND\n) | vmadm create").getOutput();
+      String specAsJson = json.toJson(specification);
+      String response = getConnection().exec("(cat <<END\n" + specAsJson + "\nEND\n) | vmadm create").getOutput();
 
       Pattern uuidPattern = Pattern.compile("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}");
       Matcher matcher = uuidPattern.matcher(response);
@@ -211,8 +165,7 @@
          String uuid = matcher.group();
          return getVM(UUID.fromString(uuid));
       } else {
-         throw new HostException("Error creating Host: response = " + response + "\n source = "
-                  + specification.toJSONSpecification());
+         throw new HostException("Error creating Host: response = " + response + "\n source = " + specAsJson);
       }
 
    }
@@ -241,7 +194,23 @@
          String line;
          ImmutableList.Builder<VM> resultBuilder = ImmutableList.builder();
          while ((line = r.readLine()) != null) {
-            VM vm = VM.builder().host(this).fromVmadmString(line).build();
+            VM vm = VM.builder().fromVmadmString(line).build();
+
+            Map<String, String> ipAddresses;
+            RateLimiter limiter = RateLimiter.create(1.0);
+            for (int i = 0; i < 30; i++) {
+               ipAddresses = getVMIpAddresses(vm.getUuid());
+               if (!ipAddresses.isEmpty()) {
+                  // Got some
+                  String ip = ipAddresses.get("net0");
+                  if (ip != null && !ip.equals("0.0.0.0")) {
+                     vm = vm.toBuilder().publicAddress(ip).build();
+                     break;
+                  }
+               }
+
+               limiter.acquire();
+            }
 
             resultBuilder.add(vm);
          }
@@ -253,7 +222,7 @@
 
    public VM getVM(UUID serverId) {
       for (VM vm : getVMs())
-         if (vm.uuid.equals(serverId))
+         if (vm.getUuid().equals(serverId))
             return vm;
       return null;
    }
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/config/SmartOSComputeServiceContextModule.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/config/SmartOSComputeServiceContextModule.java
index 124bfc9..93b9b91 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/config/SmartOSComputeServiceContextModule.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/config/SmartOSComputeServiceContextModule.java
@@ -22,8 +22,8 @@
 import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.domain.Location;
+import org.jclouds.smartos.SmartOSHostController;
 import org.jclouds.smartos.compute.domain.DataSet;
-import org.jclouds.smartos.compute.domain.SmartOSHost;
 import org.jclouds.smartos.compute.domain.VM;
 import org.jclouds.smartos.compute.domain.VmSpecification;
 import org.jclouds.smartos.compute.functions.DataSetToImage;
@@ -40,12 +40,12 @@
  * @author Nigel Magnay
  */
 public class SmartOSComputeServiceContextModule extends
-         ComputeServiceAdapterContextModule<VM, VmSpecification, DataSet, SmartOSHost> {
+         ComputeServiceAdapterContextModule<VM, VmSpecification, DataSet, SmartOSHostController> {
 
    @Override
    protected void configure() {
       super.configure();
-      bind(new TypeLiteral<ComputeServiceAdapter<VM, VmSpecification, DataSet, SmartOSHost>>() {
+      bind(new TypeLiteral<ComputeServiceAdapter<VM, VmSpecification, DataSet, SmartOSHostController>>() {
       }).to(SmartOSComputeServiceAdapter.class);
       bind(new TypeLiteral<Function<VM, NodeMetadata>>() {
       }).to(VMToNodeMetadata.class);
@@ -53,7 +53,7 @@
       }).to(DataSetToImage.class);
       bind(new TypeLiteral<Function<VmSpecification, org.jclouds.compute.domain.Hardware>>() {
       }).to(VmSpecificationToHardware.class);
-      bind(new TypeLiteral<Function<SmartOSHost, Location>>() {
+      bind(new TypeLiteral<Function<SmartOSHostController, Location>>() {
       }).to(DatacenterToLocation.class);
       // to have the compute service adapter override default locations
       // install(new LocationsFromComputeServiceAdapterModule<VM, VmSpecification, DataSet,
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/config/SmartOSParserModule.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/config/SmartOSParserModule.java
new file mode 100644
index 0000000..c0bebff
--- /dev/null
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/config/SmartOSParserModule.java
@@ -0,0 +1,66 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.smartos.compute.config;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import javax.inject.Singleton;
+
+import org.jclouds.smartos.compute.domain.DataSet;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+
+/**
+ * @author Adrian Cole
+ */
+public class SmartOSParserModule extends AbstractModule {
+
+   @Provides
+   @Singleton
+   public Map<Type, Object> provideCustomAdapterBindings() {
+      return ImmutableMap.<Type, Object> of(DataSet.class, new FlattenDataset());
+   }
+
+   public static class FlattenDataset implements JsonSerializer<DataSet>, JsonDeserializer<DataSet> {
+      @Override
+      public JsonElement serialize(DataSet dataset, Type type, JsonSerializationContext jsonSerializationContext) {
+         return new JsonPrimitive(dataset.getUuid().toString());
+      }
+
+      @Override
+      public DataSet deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+               throws JsonParseException {
+         return DataSet.builder().uuid(json.getAsString()).build();
+      }
+   }
+
+   @Override
+   protected void configure() {
+   }
+}
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/DataSet.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/DataSet.java
index be05766..5714a87 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/DataSet.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/DataSet.java
@@ -8,10 +8,6 @@
  * Dataset is a pre-built image ready to be cloned.
  */
 public class DataSet {
-   private final UUID uuid;
-   private final String os;
-   private final String published;
-   private final String urn;
 
    public static Builder builder() {
       return new Builder();
@@ -23,10 +19,10 @@
 
    public static class Builder {
 
-      public UUID uuid;
-      public String os;
-      public String published;
-      public String urn;
+      private UUID uuid;
+      private String os;
+      private String published;
+      private String urn;
 
       public Builder uuid(UUID uuid) {
          this.uuid = uuid;
@@ -73,6 +69,11 @@
       }
    }
 
+   private final UUID uuid;
+   private final String os;
+   private final String published;
+   private final String urn;
+
    protected DataSet(UUID uuid, String os, String published, String urn) {
       this.uuid = uuid;
       this.os = os;
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VM.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VM.java
index 3dac30d..0d9f67f 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VM.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VM.java
@@ -1,6 +1,5 @@
 package org.jclouds.smartos.compute.domain;
 
-import java.util.Map;
 import java.util.UUID;
 
 import com.google.common.base.Objects;
@@ -25,12 +24,12 @@
 
    public static class Builder {
 
-      protected SmartOSHost host;
-      protected UUID uuid;
-      protected String type;
-      protected String ram;
-      protected State state = State.STOPPED;
-      protected String alias;
+      private Optional<String> publicAddress = Optional.absent();
+      private UUID uuid;
+      private String type;
+      private String ram;
+      private State state = State.STOPPED;
+      private String alias;
 
       public Builder uuid(UUID uuid) {
          this.uuid = uuid;
@@ -42,8 +41,8 @@
          return this;
       }
 
-      public Builder host(SmartOSHost host) {
-         this.host = host;
+      public Builder publicAddress(String publicAddress) {
+         this.publicAddress = Optional.fromNullable(publicAddress);
          return this;
       }
 
@@ -88,24 +87,24 @@
       }
 
       public VM build() {
-         return new VM(host, uuid, type, ram, state, alias);
+         return new VM(publicAddress, uuid, type, ram, state, alias);
       }
 
       public Builder fromVM(VM in) {
-         return host(in.getHost()).uuid(in.getUuid()).type(in.getType()).ram(in.getRam()).state(in.getState())
-                  .alias(in.getAlias());
+         return publicAddress(in.getPublicAddress().orNull()).uuid(in.getUuid()).type(in.getType()).ram(in.getRam())
+                  .state(in.getState()).alias(in.getAlias());
       }
    }
 
-   protected SmartOSHost host;
-   protected final UUID uuid;
-   protected String type;
-   protected String ram;
-   protected State state;
-   protected String alias;
+   private Optional<String> publicAddress;
+   private final UUID uuid;
+   private String type;
+   private String ram;
+   private State state;
+   private String alias;
 
-   public VM(SmartOSHost host, UUID uuid, String type, String ram, State state, String alias) {
-      this.host = host;
+   protected VM(Optional<String> publicAddress, UUID uuid, String type, String ram, State state, String alias) {
+      this.publicAddress = publicAddress;
       this.uuid = uuid;
       this.type = type;
       this.ram = ram;
@@ -117,42 +116,8 @@
       return state;
    }
 
-   public void destroy() {
-      host.destroyHost(uuid);
-   }
-
-   public void reboot() {
-      host.rebootHost(uuid);
-   }
-
-   public void stop() {
-      host.stopHost(uuid);
-   }
-
-   public void start() {
-      host.startHost(uuid);
-   }
-
-   public Optional<String> getPublicAddress() throws InterruptedException {
-      Map<String, String> ipAddresses;
-
-      for (int i = 0; i < 30; i++) {
-         ipAddresses = host.getVMIpAddresses(uuid);
-         if (!ipAddresses.isEmpty()) {
-            // Got some
-            String ip = ipAddresses.get("net0");
-            if (ip != null && !ip.equals("0.0.0.0"))
-               return Optional.of(ip);
-         }
-
-         Thread.sleep(1000);
-      }
-
-      return Optional.absent();
-   }
-
-   public SmartOSHost getHost() {
-      return host;
+   public Optional<String> getPublicAddress() {
+      return publicAddress;
    }
 
    public UUID getUuid() {
@@ -191,7 +156,7 @@
          return false;
       if (getClass() != obj.getClass())
          return false;
-      return uuid.equals(((DataSet) obj).getUuid());
+      return uuid.equals(((VM) obj).getUuid());
    }
 
    /**
@@ -200,6 +165,6 @@
    @Override
    public String toString() {
       return Objects.toStringHelper(this).omitNullValues().add("uuid", uuid).add("type", type).add("ram", ram)
-               .add("alias", alias).toString();
+               .add("alias", alias).add("publicAddress", publicAddress.orNull()).toString();
    }
 }
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VmNIC.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VmNIC.java
index 0daebb8..f14a203 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VmNIC.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VmNIC.java
@@ -1,19 +1,16 @@
 package org.jclouds.smartos.compute.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Specification of a network card.
  */
 public class VmNIC {
 
-   @SerializedName("nic_tag")
-   protected final String tag;
-   protected final String ip;
-   protected final String netmask;
-   protected final String gateway;
-
    public static Builder builder() {
       return new Builder();
    }
@@ -24,10 +21,10 @@
 
    public static class Builder {
 
-      public String tag = "admin";
-      public String ip;
-      public String netmask;
-      public String gateway;
+      private String tag = "admin";
+      private String ip;
+      private String netmask;
+      private String gateway;
 
       public Builder simpleDHCPNic() {
          tag = "admin";
@@ -64,7 +61,14 @@
       }
    }
 
-   public VmNIC(String tag, String ip, String netmask, String gateway) {
+   @Named("nic_tag")
+   private final String tag;
+   private final String ip;
+   private final String netmask;
+   private final String gateway;
+
+   @ConstructorProperties({ "nic_tag", "ip", "netmask", "gateway" })
+   protected VmNIC(String tag, String ip, String netmask, String gateway) {
       this.tag = tag;
       this.ip = ip;
       this.netmask = netmask;
@@ -87,6 +91,22 @@
       return gateway;
    }
 
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(tag, ip, netmask, gateway);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null || getClass() != obj.getClass())
+         return false;
+      VmNIC that = VmNIC.class.cast(obj);
+      return Objects.equal(this.tag, that.tag) && Objects.equal(this.ip, that.ip)
+               && Objects.equal(this.netmask, that.netmask) && Objects.equal(this.gateway, that.gateway);
+   }
+
    /**
     * {@inheritDoc}
     */
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VmSpecification.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VmSpecification.java
index 9771714..9cfd1f8 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VmSpecification.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/domain/VmSpecification.java
@@ -1,32 +1,17 @@
 package org.jclouds.smartos.compute.domain;
 
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.beans.ConstructorProperties;
 import java.util.List;
 
+import javax.inject.Named;
+
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Specification of a VM to build, based on a dataset.
  */
 public class VmSpecification {
-   protected final String alias;
-   protected final String brand;
-
-   @SerializedName("dataset_uuid")
-   protected final DataSet dataset;
-   protected final String dnsDomain;
-   protected final String quota;
-
-   protected final List<VmNIC> nics;
 
    public static Builder builder() {
       return new Builder();
@@ -38,13 +23,18 @@
 
    public static class Builder {
 
-      protected String alias;
-      protected String brand = "joyent";
-      protected DataSet dataset;
-      protected String dnsDomain = "local";
-      protected String quota = "10";
+      private String alias;
+      private String brand = "joyent";
+      private DataSet dataset;
+      private String dnsDomain = "local";
+      private String quota = "0";
 
-      protected List<VmNIC> nics = new ArrayList<VmNIC>();
+      private int maxPhysicalMemory = 256;
+      private int maxLockedMemory = 256;
+      private int maxSwap = 256;
+      private int tmpFs = 256;
+
+      private ImmutableList.Builder<VmNIC> nics = ImmutableList.<VmNIC> builder();
 
       public Builder alias(String alias) {
          this.alias = alias;
@@ -71,7 +61,7 @@
          return this;
       }
 
-      public Builder nics(Collection<VmNIC> nic) {
+      public Builder nics(Iterable<VmNIC> nics) {
          this.nics.addAll(nics);
          return this;
       }
@@ -81,24 +71,78 @@
          return this;
       }
 
+      public Builder maxPhysicalMemory(int maxPhysicalMemory) {
+         this.maxPhysicalMemory = maxPhysicalMemory;
+         return this;
+      }
+
+      public Builder maxLockedMemory(int maxLockedMemory) {
+         this.maxLockedMemory = maxLockedMemory;
+         return this;
+      }
+
+      public Builder maxSwap(int maxSwap) {
+         this.maxSwap = maxSwap;
+         return this;
+      }
+
+      public Builder tmpFs(int tmpFs) {
+         this.tmpFs = tmpFs;
+         return this;
+      }
+
+      public Builder ram(int ram) {
+         this.maxPhysicalMemory = ram;
+         this.maxLockedMemory = ram;
+         this.maxSwap = ram;
+         this.tmpFs = ram;
+         return this;
+      }
+
       public VmSpecification build() {
-         return new VmSpecification(alias, brand, dataset, dnsDomain, quota, nics);
+         return new VmSpecification(alias, brand, dataset, dnsDomain, quota, maxPhysicalMemory, maxLockedMemory,
+                  maxSwap, tmpFs, nics.build());
       }
 
       public Builder fromVmSpecification(VmSpecification in) {
          return alias(in.getAlias()).brand(in.getBrand()).dataset(in.getDataset()).dnsDomain(in.getDnsDomain())
-                  .quota(in.getQuota()).nics(in.getNics());
+                  .quota(in.getQuota()).maxPhysicalMemory(in.getMaxPhysicalMemory())
+                  .maxLockedMemory(in.getMaxLockedMemory()).maxSwap(in.getMaxSwap()).tmpFs(in.getTmpFs())
+                  .nics(in.getNics());
       }
    }
 
+   private final String alias;
+   private final String brand;
+   @Named("dataset_uuid")
+   private final DataSet dataset;
+   @Named("dns_domain")
+   private final String dnsDomain;
+   private final String quota;
+   @Named("max_physical_memory")
+   private final int maxPhysicalMemory;
+   @Named("max_locked_memory")
+   private final int maxLockedMemory;
+   @Named("max_swap")
+   private final int maxSwap;
+   @Named("tmpfs")
+   private final int tmpFs;
+   private final List<VmNIC> nics;
+
+   @ConstructorProperties({ "alias", "brand", "dataset_uuid", "dns_domain", "quota", "max_physical_memory",
+            "max_locked_memory", "max_swap", "tmpfs", "nics" })
    protected VmSpecification(String alias, String brand, DataSet dataset, String dnsDomain, String quota,
-            List<VmNIC> nics) {
+            int maxPhysicalMemory, int maxLockedMemory, int maxSwap, int tmpFs, List<VmNIC> nics) {
       this.alias = alias;
       this.brand = brand;
       this.dataset = dataset;
       this.dnsDomain = dnsDomain;
       this.quota = quota;
-      this.nics = nics;
+      this.maxPhysicalMemory = maxPhysicalMemory;
+      this.maxLockedMemory = maxLockedMemory;
+      this.maxSwap = maxSwap;
+      this.tmpFs = tmpFs;
+      this.nics = ImmutableList.copyOf(nics);
    }
 
    public String getAlias() {
@@ -121,22 +165,55 @@
       return quota;
    }
 
+   public int getMaxPhysicalMemory() {
+      return maxPhysicalMemory;
+   }
+
+   public int getMaxLockedMemory() {
+      return maxLockedMemory;
+   }
+
+   public int getMaxSwap() {
+      return maxSwap;
+   }
+
+   public int getTmpFs() {
+      return tmpFs;
+   }
+
    public List<VmNIC> getNics() {
-      return ImmutableList.copyOf(nics);
+      return nics;
    }
 
-   public String toJSONSpecification() {
-      GsonBuilder gson = new GsonBuilder();
-      gson.registerTypeAdapter(DataSet.class, new FlattenDataset());
-      Gson g = gson.create();
-
-      return g.toJson(this);
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(alias, brand, dataset, dnsDomain, quota, maxPhysicalMemory, maxLockedMemory, maxSwap,
+               tmpFs, nics);
    }
 
-   public class FlattenDataset implements JsonSerializer<DataSet> {
-      @Override
-      public JsonElement serialize(DataSet vmSpecification, Type type, JsonSerializationContext jsonSerializationContext) {
-         return new JsonPrimitive(dataset.getUuid().toString());
-      }
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null || getClass() != obj.getClass())
+         return false;
+      VmSpecification that = VmSpecification.class.cast(obj);
+      return Objects.equal(this.alias, that.alias) && Objects.equal(this.brand, that.brand)
+               && Objects.equal(this.dataset, that.dataset) && Objects.equal(this.dnsDomain, that.dnsDomain)
+               && Objects.equal(this.quota, that.quota)
+               && Objects.equal(this.maxPhysicalMemory, that.maxPhysicalMemory)
+               && Objects.equal(this.maxLockedMemory, that.maxLockedMemory)
+               && Objects.equal(this.maxSwap, that.maxSwap) && Objects.equal(this.tmpFs, that.tmpFs)
+               && Objects.equal(this.nics, that.nics);
+
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("alias", alias).add("brand", brand)
+               .add("dataset_uuid", dataset != null ? dataset.getUuid() : null).add("dns_domain", dnsDomain)
+               .add("quota", quota).add("max_physical_memory", maxPhysicalMemory)
+               .add("max_locked_memory", maxLockedMemory).add("max_swap", maxSwap).add("tmpfs", tmpFs)
+               .add("nics", nics).toString();
    }
 }
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/functions/DatacenterToLocation.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/functions/DatacenterToLocation.java
index b03709d..95185cf 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/functions/DatacenterToLocation.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/functions/DatacenterToLocation.java
@@ -27,7 +27,7 @@
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LocationBuilder;
 import org.jclouds.domain.LocationScope;
-import org.jclouds.smartos.compute.domain.SmartOSHost;
+import org.jclouds.smartos.SmartOSHostController;
 
 import com.google.common.base.Function;
 import com.google.common.base.Supplier;
@@ -36,7 +36,7 @@
  * @author Nigel Magnay
  */
 @Singleton
-public class DatacenterToLocation implements Function<SmartOSHost, Location> {
+public class DatacenterToLocation implements Function<SmartOSHostController, Location> {
    private final Provider<Supplier<Location>> provider;
 
    // allow us to lazy discover the provider of a resource
@@ -46,7 +46,7 @@
    }
 
    @Override
-   public Location apply(SmartOSHost from) {
+   public Location apply(SmartOSHostController from) {
       return new LocationBuilder().scope(LocationScope.ZONE).id(from.getHostname() + "")
                .description(from.getDescription()).parent(provider.get().get()).build();
    }
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/functions/VmSpecificationToHardware.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/functions/VmSpecificationToHardware.java
index d8121c0..88ea7c1 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/functions/VmSpecificationToHardware.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/functions/VmSpecificationToHardware.java
@@ -37,10 +37,10 @@
    @Override
    public Hardware apply(VmSpecification from) {
       HardwareBuilder builder = new HardwareBuilder();
-      builder.ids("AnID");
+      builder.ids(from.getAlias());
       builder.name(from.getAlias());
       builder.processors(ImmutableList.of(new Processor(1, 1.0)));
-      builder.ram(256);
+      builder.ram(from.getMaxPhysicalMemory());
       // builder.volumes(ImmutableList.<Volume> of(new VolumeImpl(from.disk, true, false)));
       return builder.build();
    }
diff --git a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/strategy/SmartOSComputeServiceAdapter.java b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/strategy/SmartOSComputeServiceAdapter.java
index b882df7..3b01f35 100644
--- a/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/strategy/SmartOSComputeServiceAdapter.java
+++ b/labs/smartos-ssh/src/main/java/org/jclouds/smartos/compute/strategy/SmartOSComputeServiceAdapter.java
@@ -21,7 +21,8 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.inject.Inject;
@@ -31,37 +32,73 @@
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.domain.LoginCredentials;
+import org.jclouds.smartos.SmartOSHostController;
 import org.jclouds.smartos.compute.domain.DataSet;
-import org.jclouds.smartos.compute.domain.SmartOSHost;
 import org.jclouds.smartos.compute.domain.VM;
 import org.jclouds.smartos.compute.domain.VmNIC;
 import org.jclouds.smartos.compute.domain.VmSpecification;
 
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 
 /**
- * defines the connection between the {@link org.jclouds.smartos.compute.domain.SmartOSHost}
+ * defines the connection between the {@link org.jclouds.smartos.compute.domain.SmartOSHostController}
  * implementation and the jclouds {@link ComputeService}
  * 
  */
 @Singleton
-public class SmartOSComputeServiceAdapter implements ComputeServiceAdapter<VM, VmSpecification, DataSet, SmartOSHost> {
-   private final SmartOSHost host;
+public class SmartOSComputeServiceAdapter implements ComputeServiceAdapter<VM, VmSpecification, DataSet, SmartOSHostController> {
+   private final SmartOSHostController host;
+   private final Map<String, VmSpecification> specificationMap;
+
 
    @Inject
-   public SmartOSComputeServiceAdapter(SmartOSHost host) {
+   public SmartOSComputeServiceAdapter(SmartOSHostController host) {
       this.host = checkNotNull(host, "host");
+
+      Collection<VmSpecification> specifications = new ArrayList<VmSpecification>();
+
+       specifications.add(VmSpecification.builder().alias("Standard Joyent VM, 1Gb RAM / 2Gb SWAP").ram(1024).maxSwap(2048)
+               .nic(VmNIC.builder().simpleDHCPNic().build()).build());
+
+       specifications.add(VmSpecification.builder().alias("Standard Joyent VM, 2Gb RAM / 4Gb SWAP").ram(2048).maxSwap(4096)
+               .nic(VmNIC.builder().simpleDHCPNic().build()).build());
+
+       specifications.add(VmSpecification.builder().alias("Standard Joyent VM, 4Gb RAM / 8Gb SWAP").ram(4096).maxSwap(8192)
+               .nic(VmNIC.builder().simpleDHCPNic().build()).build());
+
+       specifications.add(VmSpecification.builder().alias("Standard Joyent VM, 8Gb RAM / 16Gb SWAP").ram(8192).maxSwap(16384)
+               .nic(VmNIC.builder().simpleDHCPNic().build()).build());
+
+      specificationMap = Maps.uniqueIndex(specifications, new Function<VmSpecification,String>() {
+          @Override
+          public String apply(VmSpecification input) {
+              return input.getAlias();
+          }
+      });
+
    }
 
-   private SmartOSHost getHost() {
+   private SmartOSHostController getHost() {
       return host;
    }
 
    @Override
    public NodeAndInitialCredentials<VM> createNodeWithGroupEncodedIntoName(String tag, String name, Template template) {
-      VmSpecification specification = VmSpecification.builder().alias(name)
+
+      VmSpecification.Builder builder = VmSpecification.builder();
+      String providerId = template.getHardware().getProviderId();
+
+      if( specificationMap.containsKey(providerId) ) {
+          builder.fromVmSpecification( specificationMap.get(providerId) );
+      } else {
+          builder.nic(VmNIC.builder().simpleDHCPNic().build());
+      }
+
+      VmSpecification specification = builder.alias(name)
                .dataset(getHost().getDataSet(UUID.fromString(template.getImage().getProviderId())))
-               .nic(VmNIC.builder().simpleDHCPNic().build()).build();
+               .build();
 
       VM from = getHost().createVM(specification);
 
@@ -69,16 +106,11 @@
                .password("smartos").build());
    }
 
+
+
    @Override
    public Iterable<VmSpecification> listHardwareProfiles() {
-      List<VmSpecification> specificationList = new ArrayList<VmSpecification>();
-
-      VmSpecification vs = VmSpecification.builder().alias("Standard Joyent VM")
-               .nic(VmNIC.builder().simpleDHCPNic().build()).build();
-
-      specificationList.add(vs);
-
-      return specificationList;
+      return specificationMap.values();
    }
 
    @Override
@@ -97,7 +129,7 @@
    }
 
    @Override
-   public Iterable<SmartOSHost> listLocations() {
+   public Iterable<SmartOSHostController> listLocations() {
       return ImmutableSet.of();
    }
 
@@ -108,21 +140,21 @@
 
    @Override
    public void destroyNode(String id) {
-      getHost().getVM(UUID.fromString(id)).destroy();
+      getHost().destroyHost(UUID.fromString(id));
    }
 
    @Override
    public void rebootNode(String id) {
-      getHost().getVM(UUID.fromString(id)).reboot();
+      getHost().rebootHost(UUID.fromString(id));
    }
 
    @Override
    public void resumeNode(String id) {
-      getHost().getVM(UUID.fromString(id)).start();
+      getHost().startHost(UUID.fromString(id));
    }
 
    @Override
    public void suspendNode(String id) {
-      getHost().getVM(UUID.fromString(id)).stop();
+      getHost().stopHost(UUID.fromString(id));
    }
 }
\ No newline at end of file
diff --git a/labs/smartos-ssh/src/test/java/org/jclouds/smartos/compute/parse/ParseVmSpecificationTest.java b/labs/smartos-ssh/src/test/java/org/jclouds/smartos/compute/parse/ParseVmSpecificationTest.java
new file mode 100644
index 0000000..d2b3a52
--- /dev/null
+++ b/labs/smartos-ssh/src/test/java/org/jclouds/smartos/compute/parse/ParseVmSpecificationTest.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.smartos.compute.parse;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.json.BaseItemParserTest;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.smartos.compute.config.SmartOSParserModule;
+import org.jclouds.smartos.compute.domain.DataSet;
+import org.jclouds.smartos.compute.domain.VmNIC;
+import org.jclouds.smartos.compute.domain.VmSpecification;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "ParseVmSpecificationTest")
+public class ParseVmSpecificationTest extends BaseItemParserTest<VmSpecification> {
+
+   @Override
+   public String resource() {
+      return "/vmspec.json";
+   }
+
+   @Override
+   @Consumes(MediaType.APPLICATION_JSON)
+   public VmSpecification expected() {
+      return VmSpecification.builder()
+                            .alias("small")
+                            .brand("joyent")
+                            .dataset(DataSet.builder()
+                                            .uuid("56108678-1183-11e1-83c3-ff3185a5b47f")
+                                            .os("linux")
+                                            .published("2011-11-18")
+                                            .urn("sdc:sdc:ubuntu10.04:0.1.0").build())
+                            .nic(VmNIC.builder()
+                                      .ip("192.168.1.4")
+                                      .gateway("192.168.1.1")
+                                      .netmask("255.255.255.0")
+                                      .tag("eth0").build())
+                            .dnsDomain("local")
+                            .quota("0")
+                            .maxPhysicalMemory(256)
+                            .maxLockedMemory(256)
+                            .maxSwap(256)
+                            .tmpFs(256).build();
+   }
+
+   protected Injector injector() {
+      return Guice.createInjector(new SmartOSParserModule(), new GsonModule());
+   }
+}
diff --git a/labs/smartos-ssh/src/test/resources/vmspec.json b/labs/smartos-ssh/src/test/resources/vmspec.json
new file mode 100644
index 0000000..d3bae59
--- /dev/null
+++ b/labs/smartos-ssh/src/test/resources/vmspec.json
@@ -0,0 +1,19 @@
+{
+   "alias":"small",
+   "brand":"joyent",
+   "dataset_uuid":"56108678-1183-11e1-83c3-ff3185a5b47f",
+   "dns_domain":"local",
+   "quota":"0",
+   "max_physical_memory":256,
+   "max_locked_memory":256,
+   "max_swap":256,
+   "tmpfs":256,
+   "nics":[
+      {
+         "nic_tag":"eth0",
+         "ip":"192.168.1.4",
+         "netmask":"255.255.255.0",
+         "gateway":"192.168.1.1"
+      }
+   ]
+}
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/VAppTemplate.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/VAppTemplate.java
index 7c90d7a..18a61f5 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/VAppTemplate.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/VAppTemplate.java
@@ -74,7 +74,7 @@
    
    public static abstract class Builder<B extends Builder<B>> extends ResourceEntity.Builder<B> {
       private Owner owner;
-      private Set<VAppTemplate> children = Sets.newLinkedHashSet();
+      private Set<Vm> children = Sets.newLinkedHashSet();
       private Set<SectionType> sections = Sets.newLinkedHashSet();
       private String vAppScopedLocalId;
       private Boolean ovfDescriptorUploaded;
@@ -91,7 +91,7 @@
       /**
        * @see VAppTemplate#getChildren()
        */
-      public B children(Iterable<VAppTemplate> children) {
+      public B children(Iterable<Vm> children) {
          this.children = Sets.newLinkedHashSet(checkNotNull(children, "children"));
          return self();
       }
@@ -148,7 +148,7 @@
    private Owner owner;
    @XmlElementWrapper(name = "Children")
    @XmlElement(name = "Vm")
-   private Set<VAppTemplate> children = Sets.newLinkedHashSet();
+   private Set<Vm> children = Sets.newLinkedHashSet();
    @XmlElementRefs({
       @XmlElementRef(type = VirtualHardwareSection.class),
       @XmlElementRef(type = LeaseSettingsSection.class),
@@ -179,7 +179,7 @@
    protected VAppTemplate(Builder<?> builder) {
       super(builder);
       this.owner = builder.owner;
-      this.children = builder.children.isEmpty() ? Collections.<VAppTemplate>emptySet() : ImmutableSet.copyOf(builder.children);
+      this.children = builder.children.isEmpty() ? Collections.<Vm>emptySet() : ImmutableSet.copyOf(builder.children);
       this.sections = builder.sections.isEmpty() ? null : ImmutableSet.copyOf(builder.sections);
       this.vAppScopedLocalId = builder.vAppScopedLocalId;
       this.ovfDescriptorUploaded = builder.ovfDescriptorUploaded;
@@ -200,7 +200,7 @@
    /**
     * Gets the value of the children property.
     */
-   public Set<VAppTemplate> getChildren() {
+   public Set<Vm> getChildren() {
       return children;
    }
 
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/network/NetworkConnection.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/network/NetworkConnection.java
index 54faf16..74d7b29 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/network/NetworkConnection.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/network/NetworkConnection.java
@@ -73,9 +73,9 @@
    @XmlType
    @XmlEnum(String.class)
    public static enum IpAddressAllocationMode {
-      @XmlEnumValue("pool") POOL("pool"),
-      @XmlEnumValue("dhcp") DHCP("dhcp"),
-      @XmlEnumValue("manual") MANUAL("manual"),
+      @XmlEnumValue("POOL") POOL("pool"),
+      @XmlEnumValue("DHCP") DHCP("dhcp"),
+      @XmlEnumValue("MANUAL") MANUAL("manual"),
       @XmlEnumValue("NONE") NONE("none"),
       @XmlEnumValue("") UNRECOGNIZED("unrecognized");
       
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/params/ControlAccessParams.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/params/ControlAccessParams.java
index b97fd5a..f9b9740 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/params/ControlAccessParams.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/params/ControlAccessParams.java
@@ -136,7 +136,7 @@
 	      this.accessSettings = null;
       } else {
 	      this.everyoneAccessLevel = null;
-         this.accessSettings = Iterables.isEmpty(checkNotNull(accessSettings, "accessSettings")) ? null : ImmutableList.copyOf(accessSettings);
+            this.accessSettings = Iterables.isEmpty(checkNotNull(accessSettings, "accessSettings")) ? null : ImmutableList.copyOf(accessSettings);
       }
    }
 
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApi.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApi.java
index 8cc3ab6..38df0a4 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApi.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApi.java
@@ -166,20 +166,6 @@
    CustomizationSection getCustomizationSection(URI templateUri);
 
    /**
-    * Modifies the vApp template customization information.
-    * 
-    * <pre>
-    * PUT /vAppTemplate/{id}/customizationSection
-    * </pre>
-    * 
-    * @param templateUri the URI of the template
-    * @param section the new configuration to apply
-    * @return the task performing the action. This operation is asynchronous and the user should monitor the returned
-    *         task status in order to check when it is completed.
-    */
-   Task modifyCustomizationSection(URI templateUri, CustomizationSection section);
-
-   /**
     * Retrieves the Guest Customization Section of a VM
     * 
     * <pre>
@@ -244,20 +230,6 @@
    NetworkConfigSection getNetworkConfigSection(URI templateUri);
 
    /**
-    * Modifies the network config section of a vApp.
-    * 
-    * <pre>
-    * PUT /vAppTemplate/{id}/networkConfigSection
-    * </pre>
-    * 
-    * @param templateUri the URI of the template
-    * @param section the new configuration to apply
-    * @return the task performing the action. This operation is asynchronous and the user should monitor the returned
-    *         task status in order to check when it is completed.
-    */
-   Task modifyNetworkConfigSection(URI templateUri, NetworkConfigSection section);
-
-   /**
     * Retrieves the network connection section of a VM
     * 
     * <pre>
@@ -270,20 +242,6 @@
    NetworkConnectionSection getNetworkConnectionSection(URI templateUri);
 
    /**
-    * Modifies the network connection section of a VM.
-    * 
-    * <pre>
-    * PUT /vAppTemplate/{id}/networkConnectionSection
-    * </pre>
-    * 
-    * @param templateUri the URI of the template
-    * @param section the new configuration to apply
-    * @return the task performing the action. This operation is asynchronous and the user should monitor the returned
-    *         task status in order to check when it is completed.
-    */
-   Task modifyNetworkConnectionSection(URI templateUri, NetworkConnectionSection section);
-
-   /**
     * Retrieves the network section of a vApp or vApp template.
     * 
     * <pre>
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateAsyncApi.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateAsyncApi.java
index 0e0826a..ff643b0 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateAsyncApi.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateAsyncApi.java
@@ -148,17 +148,6 @@
    ListenableFuture<CustomizationSection> getCustomizationSection(@EndpointParam URI templateURI);
 
    /**
-    * @see VAppTemplateApi#modifyCustomizationSection(URI, CustomizationSection)
-    */
-   @PUT
-   @Produces(CUSTOMIZATION_SECTION)
-   @Consumes(TASK)
-   @Path("/customizationSection")
-   @JAXBResponseParser
-   ListenableFuture<Task> modifyCustomizationSection(@EndpointParam URI templateURI,
-                                                     @BinderParam(BindToXMLPayload.class) CustomizationSection sectionType);
-
-   /**
     * @see VAppTemplateApi#getGuestCustomizationSection(URI)
     */
    @GET
@@ -221,17 +210,6 @@
    ListenableFuture<NetworkConfigSection> getNetworkConfigSection(@EndpointParam URI templateURI);
 
    /**
-    * @see VAppTemplateApi#modifyNetworkConfigSection(URI, NetworkConfigSection)
-    */
-   @PUT
-   @Produces(NETWORK_CONFIG_SECTION)
-   @Consumes(TASK)
-   @Path("/networkConfigSection")
-   @JAXBResponseParser
-   ListenableFuture<Task> modifyNetworkConfigSection(@EndpointParam URI templateURI,
-                                                     @BinderParam(BindToXMLPayload.class) NetworkConfigSection section);
-
-   /**
     * @see VAppTemplateApi#getNetworkConnectionSection(URI)
     */
    @GET
@@ -242,17 +220,6 @@
    ListenableFuture<NetworkConnectionSection> getVAppTemplateNetworkConnectionSection(@EndpointParam URI templateURI);
 
    /**
-    * @see VAppTemplateApi#modifyNetworkConnectionSection(URI, NetworkConnectionSection)
-    */
-   @PUT
-   @Produces(NETWORK_CONNECTION_SECTION)
-   @Consumes(TASK)
-   @Path("/networkConnectionSection")
-   @JAXBResponseParser
-   ListenableFuture<Task> modifyNetworkConnectionSection(@EndpointParam URI templateURI,
-                                                         @BinderParam(BindToXMLPayload.class) NetworkConnectionSection section);
-
-   /**
     * @see VAppTemplateApi#getNetworkSection(URI)
     */
    @GET
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcApi.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcApi.java
index b16c8f7..5a04955 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcApi.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/VdcApi.java
@@ -180,5 +180,4 @@
     */
    @Delegate
    MetadataApi.Readable getMetadataApi();
-
 }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/AbstractVAppApiLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/AbstractVAppApiLiveTest.java
index 81c68f6..d80d907 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/AbstractVAppApiLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/AbstractVAppApiLiveTest.java
@@ -32,6 +32,7 @@
 import java.math.BigInteger;
 import java.net.URI;
 import java.util.List;
+import java.util.Set;
 
 import org.jclouds.dmtf.cim.CimBoolean;
 import org.jclouds.dmtf.cim.CimString;
@@ -47,6 +48,9 @@
 import org.jclouds.vcloud.director.v1_5.domain.Vdc;
 import org.jclouds.vcloud.director.v1_5.domain.Vm;
 import org.jclouds.vcloud.director.v1_5.domain.dmtf.RasdItem;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConnection;
+import org.jclouds.vcloud.director.v1_5.domain.network.VAppNetworkConfiguration;
+import org.jclouds.vcloud.director.v1_5.domain.params.UndeployVAppParams;
 import org.jclouds.vcloud.director.v1_5.domain.section.GuestCustomizationSection;
 import org.jclouds.vcloud.director.v1_5.domain.section.NetworkConnectionSection;
 import org.jclouds.vcloud.director.v1_5.features.CatalogApi;
@@ -112,7 +116,6 @@
     * @see BaseVCloudDirectorApiLiveTest#setupRequiredApis()
     */
    @Override
-   @BeforeClass(alwaysRun = true, description = "Retrieves the required apis from the REST API context")
    protected void setupRequiredApis() {
       assertNotNull(context.getApi());
 
@@ -122,14 +125,13 @@
       vAppTemplateApi = context.getApi().getVAppTemplateApi();
       vdcApi = context.getApi().getVdcApi();
       vmApi = context.getApi().getVmApi();
-
-      setupEnvironment();
    }
-
+   
    /**
     * Sets up the environment. Retrieves the test {@link Vdc} and {@link VAppTemplate} from their
     * configured {@link URI}s. Instantiates a new test VApp.
     */
+   @BeforeClass(alwaysRun = true, description = "Retrieves the required apis from the REST API context")
    protected void setupEnvironment() {
       // Get the configured Vdc for the tests
       vdc = vdcApi.getVdc(vdcURI);
@@ -295,9 +297,10 @@
    protected Vm powerOffVm(final URI testVmURI) {
       Vm test = vmApi.getVm(testVmURI);
       Status status = test.getStatus();
-      if (status != Status.POWERED_OFF) {
-         Task powerOff = vmApi.powerOff(vm.getHref());
-         assertTaskSucceedsLong(powerOff);
+      if (status != Status.POWERED_OFF || test.isDeployed()) {
+         UndeployVAppParams undeployParams = UndeployVAppParams.builder().build();
+         Task shutdownVapp = vAppApi.undeploy(test.getHref(), undeployParams);
+         assertTaskSucceedsLong(shutdownVapp);
       }
       test = vmApi.getVm(testVmURI);
       assertStatus(VM, test, Status.POWERED_OFF);
@@ -372,4 +375,26 @@
          Throwables.propagate(ioe);
       }
    }
+   
+   protected VAppNetworkConfiguration getVAppNetworkConfig(VApp vApp) {
+      Set<VAppNetworkConfiguration> vAppNetworkConfigs = vAppApi.getNetworkConfigSection(vApp.getHref()).getNetworkConfigs();
+      return Iterables.tryFind(vAppNetworkConfigs, Predicates.notNull()).orNull();
+   }
+   
+   protected boolean vAppHasNetworkConfigured(VApp vApp) {
+      return getVAppNetworkConfig(vApp) != null;
+   }
+
+   protected boolean vmHasNetworkConnectionConfigured(Vm vm) {
+      return listNetworkConnections(vm).size() > 0;
+   }
+   
+   protected Set<NetworkConnection> listNetworkConnections(Vm vm) {
+      return vmApi.getNetworkConnectionSection(vm.getHref()).getNetworkConnections();
+   }
+   
+   protected Set<VAppNetworkConfiguration> listVappNetworkConfigurations(VApp vApp) {
+      Set<VAppNetworkConfiguration> vAppNetworkConfigs = vAppApi.getNetworkConfigSection(vApp.getHref()).getNetworkConfigs();
+      return vAppNetworkConfigs;
+   }
 }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java
index 725e51a..6d4bcb3 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java
@@ -691,8 +691,8 @@
       // Check optional fields
       Owner owner = template.getOwner();
       if (owner != null) checkOwner(owner);
-      for (VAppTemplate child : template.getChildren()) {
-         checkVAppTemplate(child);
+      for (Vm child : template.getChildren()) {
+         checkVm(child);
       }
       for (SectionType section : template.getSections()) {
          checkSectionType(section);
@@ -735,8 +735,8 @@
       if (params.isSharedToEveryone()) {
          assertNotNull(params.getEveryoneAccessLevel(), String.format(OBJ_FIELD_REQ, "ControlAccessParams", "EveryoneAccessLevel"));
          assertNotNull(params.getAccessSettings(), String.format(OBJ_FIELD_REQ, "ControlAccessParams", "AccessSettings when isSharedToEveryone"));
-         assertTrue(params.getAccessSettings().size() >= 1, String.format(OBJ_FIELD_GTE_1, "ControlAccessParams", "AccessSettings.size", params.getAccessSettings().size()));
       } else {
+         assertTrue(params.getAccessSettings().size() >= 1, String.format(OBJ_FIELD_GTE_1, "ControlAccessParams", "AccessSettings.size", params.getAccessSettings().size()));
          for (AccessSetting setting : params.getAccessSettings()) {
             checkAccessSetting(setting);
          }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppApiLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppApiLiveTest.java
index f30706e..3d31a13 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppApiLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppApiLiveTest.java
@@ -51,6 +51,7 @@
 import static org.testng.Assert.assertTrue;
 
 import java.net.URI;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -75,19 +76,37 @@
 import org.jclouds.vcloud.director.v1_5.domain.ResourceEntity.Status;
 import org.jclouds.vcloud.director.v1_5.domain.Task;
 import org.jclouds.vcloud.director.v1_5.domain.VApp;
+import org.jclouds.vcloud.director.v1_5.domain.VAppTemplate;
+import org.jclouds.vcloud.director.v1_5.domain.Vm;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkAssignment;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConnection;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConnection.IpAddressAllocationMode;
+import org.jclouds.vcloud.director.v1_5.domain.network.VAppNetworkConfiguration;
+import org.jclouds.vcloud.director.v1_5.domain.params.ComposeVAppParams;
 import org.jclouds.vcloud.director.v1_5.domain.params.ControlAccessParams;
 import org.jclouds.vcloud.director.v1_5.domain.params.DeployVAppParams;
+import org.jclouds.vcloud.director.v1_5.domain.params.InstantiationParams;
 import org.jclouds.vcloud.director.v1_5.domain.params.RecomposeVAppParams;
+import org.jclouds.vcloud.director.v1_5.domain.params.SourcedCompositionItemParam;
 import org.jclouds.vcloud.director.v1_5.domain.params.UndeployVAppParams;
+import org.jclouds.vcloud.director.v1_5.domain.query.QueryResultRecordType;
+import org.jclouds.vcloud.director.v1_5.domain.query.QueryResultRecords;
+import org.jclouds.vcloud.director.v1_5.domain.section.GuestCustomizationSection;
 import org.jclouds.vcloud.director.v1_5.domain.section.LeaseSettingsSection;
 import org.jclouds.vcloud.director.v1_5.domain.section.NetworkConfigSection;
+import org.jclouds.vcloud.director.v1_5.domain.section.NetworkConnectionSection;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import com.google.common.base.CharMatcher;
 import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 
 /**
  * Tests behavior of the {@link VAppApi}.
@@ -103,7 +122,7 @@
    private boolean mediaCreated = false;
    private boolean testUserCreated = false;
    
-   @BeforeClass(alwaysRun = true, dependsOnMethods = { "setupRequiredApis" })
+   @BeforeClass(alwaysRun = true)
    protected void setupRequiredEntities() {
       Set<Link> links = vdcApi.getVdc(vdcURI).getLinks();
 
@@ -190,7 +209,63 @@
       // Check status
       assertVAppStatus(vAppURI, Status.POWERED_OFF);
    }
+   
+   @Test(description = "POST /vApp/{id}/action/recomposeVApp")
+   public void testRecomposeVApp() {
+      Set<Vm> vms = getAvailableVMsFromVAppTemplates();
+  
+      VApp composedVApp = vdcApi.composeVApp(vdcURI, ComposeVAppParams.builder()
+            .name(name("composed-"))
+            .instantiationParams(instantiationParams())
+            .build());
+      
+      // get the first vm to be added to vApp
+      Vm toAddVm = Iterables.get(vms, 0);
+      RecomposeVAppParams params = createRecomposeParams(composedVApp, toAddVm); 
+      
+      // The method under test
+      Task recomposeVApp = vAppApi.recompose(composedVApp.getHref(), params);
+      assertTaskSucceedsLong(recomposeVApp);
+      
+      // add another vm instance to vApp
+      params = createRecomposeParams(composedVApp, toAddVm); 
+      recomposeVApp = vAppApi.recompose(composedVApp.getHref(), params);
+      assertTaskSucceedsLong(recomposeVApp);
+      
+      // delete a vm
+      VApp configured = vAppApi.getVApp(composedVApp.getHref());
+      List<Vm> vmsToBeDeleted = configured.getChildren().getVms();
+      Vm toBeDeleted = Iterables.get(vmsToBeDeleted, 0);
+      Task deleteVm = vmApi.deleteVm(toBeDeleted.getHref());
+      assertTaskSucceedsLong(deleteVm);
+      
+      Task deleteVApp = vAppApi.deleteVApp(composedVApp.getHref());
+      assertTaskSucceedsLong(deleteVApp);
+   }
 
+   private Set<Vm> getAvailableVMsFromVAppTemplates() {
+      Set<Vm> vms = Sets.newLinkedHashSet();
+      QueryResultRecords templatesRecords = queryApi.vAppTemplatesQueryAll();
+      for (QueryResultRecordType templateRecord : templatesRecords.getRecords()) {
+         VAppTemplate vAppTemplate = vAppTemplateApi.getVAppTemplate(templateRecord.getHref());
+         vms.addAll(vAppTemplate.getChildren());
+      }
+      return ImmutableSet.copyOf(Iterables.filter(vms, new Predicate<Vm>() {
+         // filter out vms in the vApp template with computer name that contains underscores, dots, or both.
+         @Override
+         public boolean apply(Vm input) {
+            GuestCustomizationSection guestCustomizationSection = vmApi.getGuestCustomizationSection(input.getHref());
+            String computerName = guestCustomizationSection.getComputerName();
+            String retainComputerName = CharMatcher.inRange('0', '9')
+                     .or(CharMatcher.inRange('a', 'z'))
+                     .or(CharMatcher.inRange('A', 'Z'))
+                     .or(CharMatcher.is('-'))
+                     .retainFrom(computerName);
+            return computerName.equals(retainComputerName);
+         }
+      }));
+   }
+   
    /**
     * @see VAppApi#modifyVApp(URI, VApp)
     */
@@ -217,7 +292,7 @@
    @Test(description = "POST /vApp/{id}/action/deploy", dependsOnMethods = { "testGetVApp" })
    public void testDeployVApp() {
       DeployVAppParams params = DeployVAppParams.builder()
-            .deploymentLeaseSeconds((int)TimeUnit.SECONDS.convert(1L, TimeUnit.HOURS))
+            .deploymentLeaseSeconds((int) TimeUnit.SECONDS.convert(1L, TimeUnit.HOURS))
             .notForceCustomization()
             .notPowerOn()
             .build();
@@ -274,14 +349,14 @@
       vApp = powerOnVApp(vApp.getHref());
 
       // The method under test
-      Task shutdown = vAppApi.shutdown(vAppURI);
+      Task shutdown = vAppApi.shutdown(vApp.getHref());
       assertTaskSucceedsLong(shutdown);
 
       // Get the updated VApp
-      vApp = vAppApi.getVApp(vAppURI);
+      vApp = vAppApi.getVApp(vApp.getHref());
 
       // Check status
-      assertVAppStatus(vAppURI, Status.POWERED_OFF);
+      assertVAppStatus(vApp.getHref(), Status.POWERED_OFF);
 
       // Power on the VApp again
       vApp = powerOnVApp(vApp.getHref());
@@ -378,11 +453,12 @@
 
    @Test(description = "POST /vApp/{id}/action/controlAccess", dependsOnMethods = { "testControlAccessUser" })
    public void testControlAccessEveryone() {
+      
       ControlAccessParams params = ControlAccessParams.builder()
             .sharedToEveryone()
             .everyoneAccessLevel("FullControl")
             .build();
-
+      
       // The method under test
       ControlAccessParams modified = vAppApi.modifyControlAccess(vApp.getHref(), params);
 
@@ -404,8 +480,9 @@
       assertTrue(retryTaskSuccess.apply(discardSuspendedState), String.format(TASK_COMPLETE_TIMELY, "discardSuspendedState"));
    }
 
-   @Test(description = "POST /vApp/{id}/action/enterMaintenanceMode")
+   @Test(description = "POST /vApp/{id}/action/enterMaintenanceMode", groups = {"systemAdmin"})
    public void testEnterMaintenanceMode() {
+
       // Do this to a new vApp, so don't mess up subsequent tests by making the vApp read-only
       VApp temp = instantiateVApp();
       DeployVAppParams params = DeployVAppParams.builder()
@@ -430,7 +507,7 @@
       }
    }
 
-   @Test(description = "POST /vApp/{id}/action/exitMaintenanceMode", dependsOnMethods = { "testEnterMaintenanceMode" })
+   @Test(description = "POST /vApp/{id}/action/exitMaintenanceMode", dependsOnMethods = { "testEnterMaintenanceMode" }, groups = {"systemAdmin"})
    public void testExitMaintenanceMode() {
       // Do this to a new vApp, so don't mess up subsequent tests by making the vApp read-only
       VApp temp = instantiateVApp();
@@ -456,18 +533,6 @@
       }
    }
 
-   // FIXME "Could not bind object to request[method=POST, endpoint=https://mycloud.greenhousedata.com/api/vApp/vapp-e124f3f0-adb9-4268-ad49-e54fb27e40af/action/recomposeVApp,
-   //    headers={Accept=[application/vnd.vmware.vcloud.task+xml]}, payload=[content=true, contentMetadata=[contentDisposition=null, contentEncoding=null, contentLanguage=null,
-   //    contentLength=0, contentMD5=null, contentType=application/vnd.vmware.vcloud.recomposeVAppParams+xml], written=false]]: Could not marshall object"
-   @Test(description = "POST /vApp/{id}/action/recomposeVApp", dependsOnMethods = { "testGetVApp" })
-   public void testRecomposeVApp() {
-      RecomposeVAppParams params = RecomposeVAppParams.builder().build();
-
-      // The method under test
-      Task recomposeVApp = vAppApi.recompose(vApp.getHref(), params);
-      assertTrue(retryTaskSuccess.apply(recomposeVApp), String.format(TASK_COMPLETE_TIMELY, "recomposeVApp"));
-   }
-
    @Test(description = "GET /vApp/{id}/controlAccess", dependsOnMethods = { "testGetVApp" })
    public void testGetControlAccess() {
       // The method under test
@@ -548,7 +613,8 @@
    public void testModifyNetworkConfigSection() {
       // Copy existing section and update fields
       NetworkConfigSection oldSection = vAppApi.getNetworkConfigSection(vApp.getHref());
-      NetworkConfigSection newSection = oldSection.toBuilder()
+      VAppNetworkConfiguration networkConfig = VAppNetworkConfiguration.builder().build();
+      NetworkConfigSection newSection = oldSection.toBuilder().networkConfigs(ImmutableSet.of(networkConfig))
             .build();
 
       // The method under test
@@ -640,7 +706,7 @@
       assertEquals(modified.getProductSections().size(), oldSections.getProductSections().size() + 1);
 
       // Check the section was modified correctly
-      assertEquals(modified, newSections, String.format(ENTITY_EQUAL, "ProductSectionList"));
+      assertEquals(modified, newSections);
    }
 
    @Test(description = "GET /vApp/{id}/startupSection", dependsOnMethods = { "testGetVApp" })
@@ -689,6 +755,11 @@
    
    @Test(description = "GET /vApp/{id}/metadata", dependsOnMethods = { "testSetMetadataValue" })
    public void testGetMetadata() {
+      key = name("key-");
+      String value = name("value-");
+      metadataValue = MetadataValue.builder().value(value).build();
+      vAppApi.getMetadataApi().setMetadata(vApp.getHref(), key, metadataValue);
+      
       // Call the method being tested
       Metadata metadata = vAppApi.getMetadataApi().getMetadata(vApp.getHref());
       
@@ -762,4 +833,89 @@
       VApp deleted = vAppApi.getVApp(temp.getHref());
       assertNull(deleted, "The VApp "+temp.getName()+" should have been deleted");
    }
+   
+   /**
+    * Create the recompose vapp params.
+    *
+    * @param vappTemplateRef
+    * @param vdc
+    * @return
+    * @throws VCloudException
+    */
+   public RecomposeVAppParams createRecomposeParams(VApp vApp, Vm vm) {
+
+      // creating an item element. this item will contain the vm which should be added to the vapp.
+      Reference reference = Reference.builder().name(name("vm-")).href(vm.getHref()).type(vm.getType()).build();
+      SourcedCompositionItemParam vmItem = SourcedCompositionItemParam.builder().source(reference).build();
+
+      InstantiationParams vmInstantiationParams = null;
+
+      Set<NetworkAssignment> networkAssignments = Sets.newLinkedHashSet();
+
+      // if the vm contains a network connection and the vApp does not contain any configured network
+      if (vmHasNetworkConnectionConfigured(vm)) {
+         if (!vAppHasNetworkConfigured(vApp)) {
+            // create a new network connection section for the vm.
+            NetworkConnectionSection networkConnectionSection = NetworkConnectionSection.builder()
+                     .info("Empty network configuration parameters").build();
+            // adding the network connection section to the instantiation params of the vapp.
+            vmInstantiationParams = InstantiationParams.builder().sections(ImmutableSet.of(networkConnectionSection))
+                     .build();
+         }
+
+         // if the vm already contains a network connection section and if the vapp contains a
+         // configured network -> vm could be mapped to that network.
+         else {
+            Set<VAppNetworkConfiguration> vAppNetworkConfigurations = listVappNetworkConfigurations(vApp);
+            Set<NetworkConnection> listVmNetworkConnections = listNetworkConnections(vm);
+            for (NetworkConnection networkConnection : listVmNetworkConnections) {
+               for (VAppNetworkConfiguration vAppNetworkConfiguration : vAppNetworkConfigurations) {
+                  NetworkAssignment networkAssignment = NetworkAssignment.builder()
+                           .innerNetwork(vAppNetworkConfiguration.getNetworkName())
+                           .containerNetwork(vAppNetworkConfiguration.getNetworkName()).build();
+                  networkAssignments.add(networkAssignment);
+               }
+            }
+         }
+      }
+
+      // if the vm does not contain any network connection sections and if the
+      // vapp contains a network configuration. we should add the vm to this
+      // vapp network
+      else {
+         if (vAppHasNetworkConfigured(vApp)) {
+
+            VAppNetworkConfiguration vAppNetworkConfiguration = getVAppNetworkConfig(vApp);
+            System.out.println(vAppNetworkConfiguration.getNetworkName());
+            System.out.println(vAppNetworkConfiguration.getDescription());
+
+            NetworkConnection networkConnection = NetworkConnection.builder()
+                     .network(vAppNetworkConfiguration.getNetworkName())
+                     .ipAddressAllocationMode(IpAddressAllocationMode.DHCP).build();
+
+            NetworkConnectionSection networkConnectionSection = NetworkConnectionSection.builder().info("networkInfo")
+                     .primaryNetworkConnectionIndex(0).networkConnection(networkConnection).build();
+
+            // adding the network connection section to the instantiation params of the vapp.
+            vmInstantiationParams = InstantiationParams.builder().sections(ImmutableSet.of(networkConnectionSection))
+                     .build();
+         }
+      }
+
+      if (vmInstantiationParams != null)
+         vmItem = SourcedCompositionItemParam.builder().fromSourcedCompositionItemParam(vmItem)
+                  .instantiationParams(vmInstantiationParams)
+                  .build();
+      
+      if (networkAssignments != null)
+         vmItem = SourcedCompositionItemParam.builder().fromSourcedCompositionItemParam(vmItem)
+                  .networkAssignment(networkAssignments)
+                  .build();
+      
+      return RecomposeVAppParams.builder().name(name("recompose-"))
+               // adding the vm item.
+               .sourcedItems(ImmutableList.of(vmItem)).build();
+
+   }
+
 }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppNetworksLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppNetworksLiveTest.java
new file mode 100644
index 0000000..8df1e59
--- /dev/null
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppNetworksLiveTest.java
@@ -0,0 +1,384 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.features;
+
+import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.ENTITY_NON_NULL;
+import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.TASK_COMPLETE_TIMELY;
+import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkNetworkConfigSection;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.vcloud.director.v1_5.AbstractVAppApiLiveTest;
+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.domain.Vdc;
+import org.jclouds.vcloud.director.v1_5.domain.Vm;
+import org.jclouds.vcloud.director.v1_5.domain.network.FirewallRule;
+import org.jclouds.vcloud.director.v1_5.domain.network.FirewallRuleProtocols;
+import org.jclouds.vcloud.director.v1_5.domain.network.FirewallService;
+import org.jclouds.vcloud.director.v1_5.domain.network.IpRange;
+import org.jclouds.vcloud.director.v1_5.domain.network.IpRanges;
+import org.jclouds.vcloud.director.v1_5.domain.network.IpScope;
+import org.jclouds.vcloud.director.v1_5.domain.network.NatService;
+import org.jclouds.vcloud.director.v1_5.domain.network.Network.FenceMode;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConfiguration;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConnection;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConnection.IpAddressAllocationMode;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkFeatures;
+import org.jclouds.vcloud.director.v1_5.domain.network.NetworkServiceType;
+import org.jclouds.vcloud.director.v1_5.domain.network.VAppNetworkConfiguration;
+import org.jclouds.vcloud.director.v1_5.domain.section.NetworkConfigSection;
+import org.jclouds.vcloud.director.v1_5.domain.section.NetworkConnectionSection;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Tests the request/response behavior of {@link VAppTemplateApi}
+ * 
+ * NOTE The environment MUST have at least one template configured
+ *
+ * @author Andrea Turli
+ */
+@Test(groups = { "live", "user" }, singleThreaded = true, testName = "VAppNetworksLiveTest")
+public class VAppNetworksLiveTest extends AbstractVAppApiLiveTest {
+
+   private static final String HTTP_SECURITY_GROUP = "http";
+   private static final String DEFAULT_SECURITY_GROUP = "default";
+   private String key;
+   private Reference parentNetworkRef;
+   private Map<String, NetworkConfiguration> securityGroupToNetworkConfig;
+   private String orgNetworkName;
+
+   @AfterClass(alwaysRun = true, dependsOnMethods = { "cleanUpEnvironment" })
+   protected void tidyUp() {
+      if (key != null) {
+         try {
+	         Task delete = vAppTemplateApi.getMetadataApi().deleteMetadataEntry(vAppTemplateURI, key);
+	         taskDoneEventually(delete);
+         } catch (Exception e) {
+            logger.warn(e, "Error when deleting metadata entry '%s'", key);
+         }
+      }
+   }
+   
+   @BeforeClass
+   void setUp() {
+      parentNetworkRef = lookUpNewtorkInVdc(networkURI);
+      securityGroupToNetworkConfig = createSecurityGroupToNetworkConfiguration(parentNetworkRef);
+      orgNetworkName = parentNetworkRef.getName();
+   }
+   
+   @AfterMethod
+   void cleanUpVmNetworks() {
+      disconnectVmFromVAppNetwork(vm);
+   }
+
+   @Test(description = "Create a vApp Network based on an org network with `default` firewall rules applied")
+   public void testCreateVAppNetworkWithDefaultSecurityGroup() {
+      ImmutableList<String> securityGroups = ImmutableList.of(DEFAULT_SECURITY_GROUP);
+      createVAppNetworkWithSecurityGroupOnVApp(securityGroups, vAppURI);
+
+      // Retrieve the modified section
+      NetworkConfigSection modified = vAppApi.getNetworkConfigSection(vAppURI);
+
+      // Check the retrieved object is well formed
+      checkNetworkConfigSection(modified);
+
+      /*
+       * TODO
+       * powerOn machine, ssh to it, run sshd on a port, trying to connect
+       * `which sshd` -p 22
+       *  
+       */
+   }
+   
+   @Test(description = "Create a vApp Network based on an org network with `http` firewall rules applied")
+   public void testCreateVAppNetworkWithHttpSecurityGroup() {
+      ImmutableList<String> securityGroups = ImmutableList.of(HTTP_SECURITY_GROUP);
+      createVAppNetworkWithSecurityGroupOnVApp(securityGroups, vAppURI);
+
+      // Retrieve the modified section
+      NetworkConfigSection modified = vAppApi.getNetworkConfigSection(vAppURI);
+
+      // Check the retrieved object is well formed
+      checkNetworkConfigSection(modified);
+
+      /*
+       * TODO
+       * powerOn machine, ssh to it, run sshd on a port, trying to connect
+       * `which sshd` -p 22
+       *  
+       */
+   }
+   
+   @Test(description = "Create a vApp Network based on an org network with both `defautl` and `http` firewall rules applied")
+   public void testCreateVAppNetworkWithDefaultAndHttpSecurityGroup() {
+      ImmutableList<String> securityGroups = ImmutableList.of(DEFAULT_SECURITY_GROUP, HTTP_SECURITY_GROUP);
+      createVAppNetworkWithSecurityGroupOnVApp(securityGroups, vAppURI);
+
+      // Retrieve the modified section
+      NetworkConfigSection modified = vAppApi.getNetworkConfigSection(vAppURI);
+
+      // Check the retrieved object is well formed
+      checkNetworkConfigSection(modified);
+
+      /*
+       * TODO
+       * powerOn machine, ssh to it, run sshd on a port, trying to connect
+       * `which sshd` -p 22
+       *  
+       */
+   }
+   
+   private void createVAppNetworkWithSecurityGroupOnVApp(ImmutableList<String> securityGroups, URI vAppURI) {
+      String newVAppNetworkName = generateVAppNetworkName(orgNetworkName, securityGroups);
+      // Create a vAppNetwork with firewall rules
+      NetworkConfigSection newSection = generateNetworkConfigSection(securityGroups, newVAppNetworkName);
+      Task modifyNetworkConfigSection = vAppApi.modifyNetworkConfigSection(vAppURI, newSection);
+      assertTrue(retryTaskSuccess.apply(modifyNetworkConfigSection), String.format(TASK_COMPLETE_TIMELY, "modifyNetworkConfigSection"));
+      attachVmToVAppNetwork(vm, newVAppNetworkName);
+   }
+   
+   private NetworkConfigSection generateNetworkConfigSection(List<String> securityGroups, String newVAppNetworkName) {
+      
+      Set<FirewallRule> firewallRules = Sets.newLinkedHashSet();
+      for (String securityGroup : securityGroups) {
+         Set<FirewallRule> securityGroupFirewallRules = retrieveAllFirewallRules(securityGroupToNetworkConfig.get(securityGroup).getNetworkFeatures());
+         firewallRules.addAll(securityGroupFirewallRules);
+      }
+      
+      FirewallService firewallService = createFirewallService(firewallRules);
+      NatService natService = createNatService();
+      IpScope ipScope = createNewIpScope();      
+      NetworkConfiguration newConfiguration = NetworkConfiguration.builder()
+               .ipScope(ipScope)
+               .parentNetwork(parentNetworkRef)
+               .fenceMode(FenceMode.NAT_ROUTED)
+               .retainNetInfoAcrossDeployments(false)
+               .features(createNetworkFeatures(ImmutableSet.of(firewallService, natService)))
+               .build();
+      
+      VAppNetworkConfiguration newVAppNetworkConfiguration = VAppNetworkConfiguration.builder().networkName(newVAppNetworkName).configuration(newConfiguration).build();
+      return NetworkConfigSection.builder()
+             .info("modified")
+             .networkConfigs(ImmutableSet.of(newVAppNetworkConfiguration))
+             .build();
+   }
+
+   private void attachVmToVAppNetwork(Vm vm, String vAppNetworkName) {
+      Set<NetworkConnection> networkConnections = vmApi.getNetworkConnectionSection(vm.getHref())
+               .getNetworkConnections();
+
+      NetworkConnectionSection section = NetworkConnectionSection.builder()
+               .info("info")
+               .primaryNetworkConnectionIndex(0)
+               .build();
+      
+      for (NetworkConnection networkConnection : networkConnections) {
+         NetworkConnection newNetworkConnection = networkConnection.toBuilder()
+                  .network(vAppNetworkName)
+                  .isConnected(true)
+                  .networkConnectionIndex(0)
+                  .ipAddressAllocationMode(IpAddressAllocationMode.POOL)
+                  .build();
+         
+         section = section.toBuilder().networkConnection(newNetworkConnection).build();
+      }
+      Task configureNetwork = vmApi.modifyNetworkConnectionSection(vm.getHref(), section);
+      assertTaskSucceedsLong(configureNetwork);
+   }
+
+   private IpScope createNewIpScope() {
+      IpRange newIpRange = createIpRange();
+      IpRanges newIpRanges = IpRanges.builder()
+               .ipRange(newIpRange)
+               .build();
+      return IpScope.builder()
+               .isInherited(false)
+               .gateway("192.168.2.1")
+               .netmask("255.255.0.0")
+               .ipRanges(newIpRanges).build();
+   }
+
+   private IpRange createIpRange() {
+      IpRange newIpRange = IpRange.builder()
+               .startAddress("192.168.2.100")
+               .endAddress("192.168.2.199")
+               .build();
+      return newIpRange;
+   }
+
+   private Reference lookUpNewtorkInVdc(final URI networkURI) {
+      Vdc vdc = context.getApi().getVdcApi().getVdc(vdcURI);
+      assertNotNull(vdc, String.format(ENTITY_NON_NULL, VDC));
+
+      Set<Reference> networks = vdc.getAvailableNetworks();
+
+      // Look up the network in the Vdc with the id configured for the tests
+      Optional<Reference> parentNetwork = Iterables.tryFind(networks, new Predicate<Reference>() {
+         @Override
+         public boolean apply(Reference reference) {
+            return reference.getHref().equals(networkURI);
+         }
+      });
+      if (!parentNetwork.isPresent()) {
+         fail(String.format("Could not find network %s in vdc", networkURI.toASCIIString()));
+      }
+      return parentNetwork.get();
+   }
+
+   private Set<FirewallRule> retrieveAllFirewallRules(NetworkFeatures networkFeatures) {
+      Set<FirewallRule> firewallRules = Sets.newLinkedHashSet();
+      for (NetworkServiceType<?> networkServiceType : networkFeatures.getNetworkServices()) {
+         if (networkServiceType instanceof FirewallService) {
+            firewallRules.addAll(((FirewallService) networkServiceType).getFirewallRules());
+         }
+      }
+      return firewallRules;
+   }
+
+   private NetworkFeatures createNetworkFeatures(Set<? extends NetworkServiceType<?>> networkServices) {
+      NetworkFeatures networkFeatures = NetworkFeatures.builder()
+               .services(networkServices)
+               .build();
+      return networkFeatures;
+   }
+
+
+   private Set<FirewallRule> createDefaultFirewallRules() {
+      FirewallRuleProtocols protocols = FirewallRuleProtocols.builder()
+               .any(true)
+               .build();
+      FirewallRule egressAll = createFirewallRule(FirewallRuleProtocols.builder().tcp(true).build(), "allow ssh ingoing traffic", -1, 22, "in");
+      FirewallRule sshIngoing = createFirewallRule(protocols, "allow all outgoing traffic", -1, -1, "out");
+      return ImmutableSet.of(egressAll, sshIngoing);
+   }
+
+   private Set<FirewallRule> createHttpIngoingFirewallRule() {
+      FirewallRuleProtocols protocols = FirewallRuleProtocols.builder().tcp(true).build();
+      FirewallRule httpIngoing = createFirewallRule(protocols , "allow http ingoing traffic", 80, 80, "in");
+      FirewallRule httpsIngoing = createFirewallRule(protocols , "allow https ingoing traffic", 443, 443, "in");
+      return ImmutableSet.of(httpIngoing, httpsIngoing);
+   }
+   
+   private FirewallRule createFirewallRule(FirewallRuleProtocols protocols, String description, int sourcePort, int outPort, String direction) {
+      return FirewallRule.builder()
+               .isEnabled(true)
+               .description(description)
+               .policy("allow")
+               .protocols(protocols)
+               .port(outPort)
+               .destinationIp("Any")
+               .sourcePort(sourcePort)
+               .sourceIp("Any")
+               .direction(direction)
+               .enableLogging(false)
+               .build();
+   }
+   
+   private FirewallService createFirewallService(Set<FirewallRule> firewallRules) {
+      FirewallService firewallService = FirewallService.builder()
+               .enabled(true)
+               .defaultAction("drop")
+               .logDefaultAction(false)
+               .firewallRules(firewallRules)
+               .build();
+      return firewallService;
+   }
+   
+   private NatService createNatService() {
+      NatService natService = NatService.builder()
+               .enabled(true)
+               .natType("ipTranslation")
+               .policy("allowTraffic")
+               .build();
+      return natService;
+   }
+   
+   private Map<String, NetworkConfiguration> createSecurityGroupToNetworkConfiguration(Reference parentNetworkRef) {
+      Set<FirewallRule> defaultFirewallRules = createDefaultFirewallRules();
+      Set<FirewallRule> httpFirewallRules = createHttpIngoingFirewallRule();
+
+      Map<String, NetworkConfiguration> securityGroupToNetworkConfigurations = Maps.newHashMap();
+      securityGroupToNetworkConfigurations.put(DEFAULT_SECURITY_GROUP, createNetworkConfiguration(parentNetworkRef, defaultFirewallRules));
+      securityGroupToNetworkConfigurations.put(HTTP_SECURITY_GROUP, createNetworkConfiguration(parentNetworkRef, httpFirewallRules));
+      
+      return securityGroupToNetworkConfigurations;
+   }
+
+   private NetworkConfiguration createNetworkConfiguration(Reference parentNetworkRef, Set<FirewallRule> newFirewallRules) {
+      FirewallService firewallService = createFirewallService(newFirewallRules);
+
+      IpScope ipScope = createNewIpScope();      
+
+      NetworkConfiguration newConfiguration = NetworkConfiguration.builder()
+               .ipScope(ipScope)
+               .parentNetwork(parentNetworkRef)
+               .fenceMode(FenceMode.NAT_ROUTED)
+               .retainNetInfoAcrossDeployments(false)
+               .features(createNetworkFeatures(ImmutableSet.of(firewallService)))
+               .build();
+      return newConfiguration;
+   }
+   
+   private static String generateVAppNetworkName(String orgNetworkName, List<String> securityGroupNames) {
+      return orgNetworkName + "-" + Joiner.on("+").join(securityGroupNames);
+   }
+
+   private void disconnectVmFromVAppNetwork(Vm vm) {
+      
+      Set<NetworkConnection> networkConnections = vmApi.getNetworkConnectionSection(vm.getHref())
+               .getNetworkConnections();
+
+      NetworkConnectionSection section = NetworkConnectionSection.builder()
+               .info("info")
+               .primaryNetworkConnectionIndex(0)
+               .build();
+
+      for (NetworkConnection networkConnection : networkConnections) {
+         section = section
+                  .toBuilder()
+                  .networkConnection(networkConnection.toBuilder()
+                           .network("none")
+                           .ipAddressAllocationMode(IpAddressAllocationMode.NONE)
+                           .build())
+                  .build();
+      } 
+      Task cleanUpNetworks = vmApi.modifyNetworkConnectionSection(vm.getHref(), section);
+      assertTaskSucceedsLong(cleanUpNetworks);
+   }
+
+}
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApiExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApiExpectTest.java
index 36a7a8b..1fc5387 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApiExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApiExpectTest.java
@@ -49,24 +49,11 @@
 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.domain.VAppTemplate;
-import org.jclouds.vcloud.director.v1_5.domain.network.FirewallRule;
-import org.jclouds.vcloud.director.v1_5.domain.network.FirewallRuleProtocols;
-import org.jclouds.vcloud.director.v1_5.domain.network.FirewallService;
-import org.jclouds.vcloud.director.v1_5.domain.network.IpRange;
-import org.jclouds.vcloud.director.v1_5.domain.network.IpRanges;
-import org.jclouds.vcloud.director.v1_5.domain.network.IpScope;
-import org.jclouds.vcloud.director.v1_5.domain.network.NatOneToOneVmRule;
-import org.jclouds.vcloud.director.v1_5.domain.network.NatRule;
-import org.jclouds.vcloud.director.v1_5.domain.network.NatService;
-import org.jclouds.vcloud.director.v1_5.domain.network.Network.FenceMode;
-import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConfiguration;
-import org.jclouds.vcloud.director.v1_5.domain.network.NetworkFeatures;
-import org.jclouds.vcloud.director.v1_5.domain.network.VAppNetworkConfiguration;
+import org.jclouds.vcloud.director.v1_5.domain.Vm;
 import org.jclouds.vcloud.director.v1_5.domain.params.RelocateParams;
 import org.jclouds.vcloud.director.v1_5.domain.section.CustomizationSection;
 import org.jclouds.vcloud.director.v1_5.domain.section.GuestCustomizationSection;
 import org.jclouds.vcloud.director.v1_5.domain.section.LeaseSettingsSection;
-import org.jclouds.vcloud.director.v1_5.domain.section.NetworkConfigSection;
 import org.jclouds.vcloud.director.v1_5.internal.VCloudDirectorAdminApiExpectTest;
 import org.testng.annotations.Test;
 
@@ -256,27 +243,6 @@
       api.relocateVm(uri, params);
    }
 
-   @Test
-   public void testCustomizationSection() {
-      final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9";
-      URI uri = URI.create(endpoint + templateId);
-
-      VAppTemplateApi api = orderedRequestsSendResponses(loginRequest, sessionResponse,
-            new VcloudHttpRequestPrimer().apiCommand("GET", templateId + "/customizationSection").acceptMedia(CUSTOMIZATION_SECTION).httpRequestBuilder().build(),
-            new VcloudHttpResponsePrimer().xmlFilePayload("/vapptemplate/customizationSection.xml", CUSTOMIZATION_SECTION).httpResponseBuilder().build(),
-            new VcloudHttpRequestPrimer().apiCommand("PUT", templateId + "/customizationSection").xmlFilePayload("/vapptemplate/customizationSection.xml", CUSTOMIZATION_SECTION).acceptMedia(TASK).httpRequestBuilder().build(),
-            new VcloudHttpResponsePrimer().xmlFilePayload("/task/task.xml", TASK).httpResponseBuilder().build()
-      ).getVAppTemplateApi();
-
-      assertNotNull(api);
-      CustomizationSection section = api.getCustomizationSection(uri);
-
-      assertEquals(section, exampleCustomizationSection());
-
-      Task task = api.modifyCustomizationSection(uri, exampleCustomizationSection());
-      assertNotNull(task);
-   }
-
    public void testErrorGetCustomizationSection() {
       final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9";
       URI uri = URI.create(endpoint + templateId);
@@ -288,18 +254,6 @@
       assertNull(api.getCustomizationSection(uri));
    }
    
-   @Test(expectedExceptions = ResourceNotFoundException.class)
-   public void testErrorEditCustomizationSection() {
-      final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9";
-      URI uri = URI.create(endpoint + templateId);
-
-      VAppTemplateApi api = orderedRequestsSendResponses(loginRequest, sessionResponse,
-            new VcloudHttpRequestPrimer().apiCommand("PUT", templateId + "/customizationSection").xmlFilePayload("/vapptemplate/customizationSection.xml", CUSTOMIZATION_SECTION).acceptMedia(TASK).httpRequestBuilder().build(),
-            new VcloudHttpResponsePrimer().xmlFilePayload("/vapptemplate/error403.xml", ERROR).httpResponseBuilder().statusCode(403).build()).getVAppTemplateApi();
-
-      api.modifyCustomizationSection(uri, exampleCustomizationSection());
-   }
-   
    public void testGuestCustomizationSection() {
       final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9";
       URI uri = URI.create(endpoint + templateId);
@@ -490,27 +444,6 @@
 
       api.getMetadataApi().deleteMetadataEntry(uri, "12345");
    }
-   
-   public void testNetworkConfigSection() throws ParseException {
-      final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9";
-      URI uri = URI.create(endpoint + templateId);
-
-      VAppTemplateApi api = orderedRequestsSendResponses(loginRequest, sessionResponse,
-            new VcloudHttpRequestPrimer().apiCommand("GET", templateId + "/networkConfigSection").acceptMedia(NETWORK_CONFIG_SECTION).httpRequestBuilder().build(),
-            new VcloudHttpResponsePrimer().xmlFilePayload("/vapptemplate/networkConfigSection.xml", NETWORK_CONFIG_SECTION).httpResponseBuilder().build(),
-            new VcloudHttpRequestPrimer().apiCommand("PUT", templateId + "/networkConfigSection").xmlFilePayload("/vapptemplate/networkConfigSection.xml", NETWORK_CONFIG_SECTION).acceptMedia(TASK).httpRequestBuilder().build(),
-            new VcloudHttpResponsePrimer().xmlFilePayload("/task/task.xml", TASK).httpResponseBuilder().build()
-      ).getVAppTemplateApi();
-
-      assertNotNull(api);
-
-      NetworkConfigSection section = api.getNetworkConfigSection(uri);
-
-      assertEquals(section, exampleNetworkConfigSection());
-
-      Task task = api.modifyNetworkConfigSection(uri, exampleNetworkConfigSection());
-      assertNotNull(task);
-   }
 
    @Test(expectedExceptions = VCloudDirectorException.class)
    public void testErrorGetNetworkConfigSection() {
@@ -524,18 +457,6 @@
       api.getNetworkConfigSection(uri);
    }
 
-   @Test(expectedExceptions = VCloudDirectorException.class)
-   public void testErrorEditNetworkConfigSection() throws ParseException {
-      final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9";
-      URI uri = URI.create(endpoint + templateId);
-
-      VAppTemplateApi api = orderedRequestsSendResponses(loginRequest, sessionResponse,
-            new VcloudHttpRequestPrimer().apiCommand("PUT", templateId + "/networkConfigSection").xmlFilePayload("/vapptemplate/networkConfigSection.xml", NETWORK_CONFIG_SECTION).acceptMedia(TASK).httpRequestBuilder().build(),
-            new VcloudHttpResponsePrimer().xmlFilePayload("/vapptemplate/error400.xml", ERROR).httpResponseBuilder().statusCode(400).build()).getVAppTemplateApi();
-
-      api.modifyNetworkConfigSection(uri, exampleNetworkConfigSection());
-   }
-
    private VAppTemplate exampleTemplate() {
       Link aLink = Link.builder().href(URI.create("https://vcloudbeta.bluelock.com/api/vdc/d16d333b-e3c0-4176-845d-a5ee6392df07"))
             .type("application/vnd.vmware.vcloud.vdc+xml").rel("up").build();
@@ -562,7 +483,7 @@
 
       return VAppTemplate.builder().href(URI.create("https://vcloudbeta.bluelock.com/api/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9"))
             .links(ImmutableSet.of(aLink, bLink))
-            .children(ImmutableSet.<VAppTemplate>of())
+            .children(ImmutableSet.<Vm>of())
             .type("application/vnd.vmware.vcloud.vAppTemplate+xml")
             .description("For testing")
             .id("urn:vcloud:vapptemplate:ef4415e6-d413-4cbb-9262-f9bbec5f2ea9")
@@ -575,20 +496,6 @@
             .build();
    }
 
-   private CustomizationSection exampleCustomizationSection() {
-      return CustomizationSection.builder()
-            .links(ImmutableSet.of(
-                  Link.builder().href(URI.create("https://vcloudbeta.bluelock.com/api/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9/customizationSection/"))
-                        .type("application/vnd.vmware.vcloud.customizationSection+xml").rel("edit").build()
-            ))
-            .type("application/vnd.vmware.vcloud.customizationSection+xml")
-            .info("VApp template customization section")
-            .customizeOnInstantiate(true)
-            .href(URI.create("https://vcloudbeta.bluelock.com/api/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9/customizationSection/"))
-            .required(false)
-            .build();
-   }
-
    private GuestCustomizationSection exampleGuestCustomizationSection() {
       return GuestCustomizationSection.builder()
             .links(ImmutableSet.of(
@@ -640,59 +547,4 @@
       return MetadataValue.builder().value("some value").build();
    }
 
-   private NetworkConfigSection exampleNetworkConfigSection() throws ParseException {
-      
-      FirewallService firewallService =
-            FirewallService.builder()
-                  .enabled(true)
-                  .firewallRules(
-                  ImmutableSet.of(
-                        FirewallRule.builder()
-                              .isEnabled(true)
-                              .description("FTP Rule")
-                              .policy("allow")
-                              .protocols(FirewallRuleProtocols.builder().tcp(true).build())
-                              .port(21)
-                              .destinationIp("10.147.115.1")
-                              .build(),
-                        FirewallRule.builder()
-                              .isEnabled(true)
-                              .description("SSH Rule")
-                              .policy("allow")
-                              .protocols(FirewallRuleProtocols.builder().tcp(true).build())
-                              .port(22)
-                              .destinationIp("10.147.115.1")
-                              .build())).build();
-
-      NatService natService = NatService.builder().enabled(true).natType("ipTranslation").policy("allowTraffic")
-            .natRules(ImmutableSet.of(NatRule.builder().oneToOneVmRule(
-                  NatOneToOneVmRule.builder().mappingMode("manual").externalIpAddress("64.100.10.1").vAppScopedVmId("20ea086f-1a6a-4fb2-8e2e-23372facf7de").vmNicId(0).build()).build()
-            )).build();
-
-      NetworkConfiguration networkConfiguration = NetworkConfiguration.builder().ipScope(
-            IpScope.builder()
-                  .isInherited(false)
-                  .gateway("10.147.56.253")
-                  .netmask("255.255.255.0")
-                  .dns1("10.147.115.1")
-                  .dns2("10.147.115.2")
-                  .dnsSuffix("example.com")
-                  .ipRanges(IpRanges.builder().ipRange(IpRange.builder().startAddress("10.147.56.1").endAddress("10.147.56.1").build()).build())
-                  .build())
-            .parentNetwork(Reference.builder().href(URI.create("http://vcloud.example.com/api/v1.0/network/54")).type("application/vnd.vmware.vcloud.network+xml").name("Internet").build())
-            .fenceMode(FenceMode.NAT_ROUTED)
-            .features(NetworkFeatures.builder().services(ImmutableSet.of(firewallService, natService)).build())
-            .build();
-      
-      return NetworkConfigSection.builder()
-            .info("Configuration parameters for logical networks")
-            .networkConfigs(
-                  ImmutableSet.of(
-                        VAppNetworkConfiguration.builder()
-                              .networkName("vAppNetwork")
-                              .configuration(
-                                    networkConfiguration
-                              ).build()
-                  )).build();
-   }
 }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApiLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApiLiveTest.java
index e0e2c04..40cf401 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApiLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateApiLiveTest.java
@@ -26,7 +26,6 @@
 import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkMetadataKeyAbsentFor;
 import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkMetadataValue;
 import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkNetworkConfigSection;
-import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkNetworkConnectionSection;
 import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkOvfEnvelope;
 import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkOvfNetworkSection;
 import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkOwner;
@@ -60,8 +59,6 @@
 import org.jclouds.vcloud.director.v1_5.domain.Task;
 import org.jclouds.vcloud.director.v1_5.domain.VAppTemplate;
 import org.jclouds.vcloud.director.v1_5.domain.dmtf.Envelope;
-import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConnection;
-import org.jclouds.vcloud.director.v1_5.domain.network.NetworkConnection.IpAddressAllocationMode;
 import org.jclouds.vcloud.director.v1_5.domain.params.CloneVAppTemplateParams;
 import org.jclouds.vcloud.director.v1_5.domain.params.RelocateParams;
 import org.jclouds.vcloud.director.v1_5.domain.section.CustomizationSection;
@@ -89,7 +86,7 @@
 
    private String key;
    private String val;
-   
+
    @AfterClass(alwaysRun = true, dependsOnMethods = { "cleanUpEnvironment" })
    protected void tidyUp() {
       if (key != null) {
@@ -101,11 +98,12 @@
          }
       }
    }
-
-   // FIXME cloneVAppTemplate is giving back 500 error
+   
    private VAppTemplate cloneVAppTemplate(boolean waitForTask) throws Exception {
       CloneVAppTemplateParams cloneVAppTemplateParams = CloneVAppTemplateParams.builder()
                .source(Reference.builder().href(vAppTemplateURI).build())
+               .isSourceDelete(false)
+               .name("clone")
                .build();
       VAppTemplate clonedVappTemplate = vdcApi.cloneVAppTemplate(vdcURI, cloneVAppTemplateParams);
       
@@ -114,7 +112,6 @@
          assertNotNull(cloneTask, "vdcApi.cloneVAppTemplate returned VAppTemplate that did not contain any tasks");
          assertTaskSucceeds(cloneTask);
       }
-
       return clonedVappTemplate;
    }
 
@@ -306,34 +303,15 @@
       assertEquals(modified.getComputerName(), computerName);
    }
    
-   @Test(description = "PUT /vAppTemplate/{id}/customizationSection")
-   public void testEditCustomizationSection() {
-      boolean oldVal = vAppTemplateApi.getCustomizationSection(vAppTemplateURI).isCustomizeOnInstantiate();
-      boolean newVal = !oldVal;
-      
-      CustomizationSection customizationSection = CustomizationSection.builder()
-               .info("")
-               .customizeOnInstantiate(newVal)
-               .build();
-      
-      final Task task = vAppTemplateApi.modifyCustomizationSection(vAppTemplateURI, customizationSection);
-      assertTaskSucceeds(task);
-
-      CustomizationSection newCustomizationSection = vAppTemplateApi.getCustomizationSection(vAppTemplateURI);
-      assertEquals(newCustomizationSection.isCustomizeOnInstantiate(), newVal);
-   }
-
-   // FIXME deploymentLeaseInSeconds returned is null
+   // NOTE vAppTemplate supports only storageLease (deployment lease applies to vApp too)
    @Test(description = "PUT /vAppTemplate/{id}/leaseSettingsSection")
    public void testEditLeaseSettingsSection() throws Exception {
-      int deploymentLeaseInSeconds = random.nextInt(10000)+1;
       // NOTE use smallish number for storageLeaseInSeconds; it seems to be capped at 5184000?
       int storageLeaseInSeconds = random.nextInt(10000)+1;
 
       LeaseSettingsSection leaseSettingSection = LeaseSettingsSection.builder()
                .info("my info")
                .storageLeaseInSeconds(storageLeaseInSeconds)
-               .deploymentLeaseInSeconds(deploymentLeaseInSeconds)
                .build();
       
       final Task task = vAppTemplateApi.modifyLeaseSettingsSection(vAppTemplateURI, leaseSettingSection);
@@ -341,65 +319,8 @@
       
       LeaseSettingsSection newLeaseSettingsSection = vAppTemplateApi.getLeaseSettingsSection(vAppTemplateURI);
       assertEquals(newLeaseSettingsSection.getStorageLeaseInSeconds(), (Integer) storageLeaseInSeconds);
-      assertEquals(newLeaseSettingsSection.getDeploymentLeaseInSeconds(), (Integer) deploymentLeaseInSeconds);
    }
 
-   @Test(description = "PUT /vAppTemplate/{id}/networkConfigSection")
-   public void testEditNetworkConfigSection() {
-      // TODO What to modify?
-      
-      NetworkConfigSection oldSection = vAppTemplateApi.getNetworkConfigSection(vApp.getHref());
-      NetworkConfigSection newSection = oldSection.toBuilder().build();
-      
-//      String networkName = ""+random.nextInt();
-//      NetworkConfiguration networkConfiguration = NetworkConfiguration.builder()
-//               .fenceMode("isolated")
-//               .build();
-//      VAppNetworkConfiguration vappNetworkConfiguration = VAppNetworkConfiguration.builder()
-//               .networkName(networkName)
-//               .configuration(networkConfiguration)
-//               .build();
-//      Set<VAppNetworkConfiguration> vappNetworkConfigurations = ImmutableSet.of(vappNetworkConfiguration);
-//      NetworkConfigSection networkConfigSection = NetworkConfigSection.builder()
-//               .info("my info")
-//               .networkConfigs(vappNetworkConfigurations)
-//               .build();
-      
-      final Task task = vAppTemplateApi.modifyNetworkConfigSection(vApp.getHref(), newSection);
-      assertTaskSucceeds(task);
-
-      NetworkConfigSection modified = vAppTemplateApi.getNetworkConfigSection(vAppTemplateURI);
-      checkNetworkConfigSection(modified);
-
-//      assertEquals(modified§.getNetworkConfigs().size(), 1);
-//      
-//      VAppNetworkConfiguration newVAppNetworkConfig = Iterables.get(modified§.getNetworkConfigs(), 0);
-//      assertEquals(newVAppNetworkConfig.getNetworkName(), networkName);
-   }
-
-   @Test(description = "PUT /vAppTemplate/{id}/networkConnectionSection")
-   public void testEditNetworkConnectionSection() {
-      // Look up a network in the Vdc
-      Set<Reference> networks = vdc.getAvailableNetworks();
-      Reference network = Iterables.getLast(networks);
-
-      // Copy existing section and update fields
-      NetworkConnectionSection oldSection = vAppTemplateApi.getNetworkConnectionSection(vm.getHref());
-      NetworkConnectionSection newSection = oldSection.toBuilder()
-            .networkConnection(NetworkConnection.builder()
-                  .ipAddressAllocationMode(IpAddressAllocationMode.DHCP.toString())
-                  .network(network.getName())
-                  .build())
-            .build();
-      
-      Task task = vAppTemplateApi.modifyNetworkConnectionSection(vm.getHref(), newSection);
-      assertTaskSucceeds(task);
-
-      NetworkConnectionSection modified = vAppTemplateApi.getNetworkConnectionSection(vm.getHref());
-      checkNetworkConnectionSection(modified);
-   }
-   
-   // FIXME cloneVAppTemplate is giving back 500 error
    @Test(description = "DELETE /vAppTemplate/{id}", dependsOnMethods = { "testGetVAppTemplate" }) 
    public void testDeleteVAppTemplate() throws Exception {
       VAppTemplate clonedVappTemplate = cloneVAppTemplate(true);
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcApiExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcApiExpectTest.java
index 1ae6344..8ba976f 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcApiExpectTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcApiExpectTest.java
@@ -23,6 +23,9 @@
 import static org.testng.Assert.fail;
 
 import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
 
 import org.jclouds.vcloud.director.v1_5.VCloudDirectorException;
 import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
@@ -32,6 +35,9 @@
 import org.jclouds.vcloud.director.v1_5.domain.Error;
 import org.jclouds.vcloud.director.v1_5.domain.Link;
 import org.jclouds.vcloud.director.v1_5.domain.Media;
+import org.jclouds.vcloud.director.v1_5.domain.Owner;
+import org.jclouds.vcloud.director.v1_5.domain.Task;
+import org.jclouds.vcloud.director.v1_5.domain.User;
 import org.jclouds.vcloud.director.v1_5.domain.Media.ImageType;
 import org.jclouds.vcloud.director.v1_5.domain.Metadata;
 import org.jclouds.vcloud.director.v1_5.domain.MetadataValue;
@@ -39,6 +45,7 @@
 import org.jclouds.vcloud.director.v1_5.domain.VApp;
 import org.jclouds.vcloud.director.v1_5.domain.VAppTemplate;
 import org.jclouds.vcloud.director.v1_5.domain.Vdc;
+import org.jclouds.vcloud.director.v1_5.domain.org.Org;
 import org.jclouds.vcloud.director.v1_5.domain.params.CaptureVAppParams;
 import org.jclouds.vcloud.director.v1_5.domain.params.CloneMediaParams;
 import org.jclouds.vcloud.director.v1_5.domain.params.CloneVAppParams;
@@ -217,11 +224,11 @@
    }
 
    @Test(enabled = false)
-   public void testComposeVApp() {
+   public void testComposeVApp() throws ParseException {
       VCloudDirectorApi api = requestsSendResponses(loginRequest, sessionResponse, 
             new VcloudHttpRequestPrimer()
                .apiCommand("POST", "/vdc/e9cd3387-ac57-4d27-a481-9bee75e0690f/action/composeVApp")
-               .xmlFilePayload("/vdc/params/composeVApp.xml", VCloudDirectorMediaType.COMPOSE_VAPP_PARAMS)
+               .xmlFilePayload("/vdc/composeVAppParams.xml", VCloudDirectorMediaType.COMPOSE_VAPP_PARAMS)
                .acceptAnyMedia()
                .httpRequestBuilder().build(), 
             new VcloudHttpResponsePrimer()
@@ -513,11 +520,58 @@
       return null;
    }
    
-   private VApp composeVApp() {
-      // TODO Auto-generated method stub
-      return null;
+   private VApp composeVApp() throws ParseException {
+
+      return VApp
+               .builder()
+               .status(0)
+               .name("test-vapp")
+               .id("urn:vcloud:vapp:d0e2b6b9-4381-4ddc-9572-cdfae54059be")
+               .type("application/vnd.vmware.vcloud.vApp+xml")
+               .description("Test VApp")
+               .href(URI.create("https://mycloud.greenhousedata.com/api/vApp/vapp-d0e2b6b9-4381-4ddc-9572-cdfae54059be"))
+               .link(Link
+                        .builder()
+                        .name("orgNet-cloudsoft-External")
+                        .rel("down")
+                        .type("application/vnd.vmware.vcloud.vAppNetwork+xml")
+                        .href(URI.create("https://mycloud.greenhousedata.com/api/network/2a2e2da4-446a-4ebc-a086-06df7c9570f0"))
+                        .build())
+               .link(Link
+                        .builder()
+                        .rel("down")
+                        .type("application/vnd.vmware.vcloud.controlAccess+xml")
+                        .href(URI.create("https://mycloud.greenhousedata.com/api/vApp/vapp-d0e2b6b9-4381-4ddc-9572-cdfae54059be/controlAccess/"))
+                        .build())
+               .link(Link
+                        .builder()
+                        .rel("up")
+                        .type("application/vnd.vmware.vcloud.vdc+xml")
+                        .href(URI.create("https://mycloud.greenhousedata.com/api/vdc/e9cd3387-ac57-4d27-a481-9bee75e0690f"))
+                        .build())
+               .link(Link
+                        .builder()
+                        .rel("down")
+                        .type("application/vnd.vmware.vcloud.owner+xml")
+                        .href(URI.create("https://mycloud.greenhousedata.com/api/vApp/vapp-d0e2b6b9-4381-4ddc-9572-cdfae54059be/owner"))
+                        .build())
+               .link(Link
+                        .builder()
+                        .rel("down")
+                        .type("application/vnd.vmware.vcloud.metadata+xml")
+                        .href(URI.create("https://mycloud.greenhousedata.com/api/vApp/vapp-d0e2b6b9-4381-4ddc-9572-cdfae54059be/metadata"))
+                        .build())
+               .owner(Owner
+                        .builder()
+                        .type("application/vnd.vmware.vcloud.owner+xml")
+                        .user(User
+                                 .builder()
+                                 .type("application/vnd.vmware.admin.user+xml")
+                                 .name("acole")
+                                 .href(URI.create("https://mycloud.greenhousedata.com/api/admin/user/c090335b-708c-4c1c-9e3d-89560d002120"))
+                                 .build().getRole()).build()).build();
    }
-   
+
    private VApp instantiateVAppTemplate() {
       // TODO Auto-generated method stub
       return null;
@@ -537,4 +591,5 @@
       // TODO Auto-generated method stub
       return null;
    }
+   
 }
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VmApiLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VmApiLiveTest.java
index 27b1223..0e65613 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VmApiLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VmApiLiveTest.java
@@ -121,7 +121,7 @@
    private boolean mediaCreated = false;
    private boolean testUserCreated = false;
    
-   @BeforeClass(alwaysRun = true, dependsOnMethods = { "setupRequiredApis" })
+   @BeforeClass(alwaysRun = true)
    protected void setupRequiredEntities() {
       Set<Link> links = vdcApi.getVdc(vdcURI).getLinks();
 
@@ -280,23 +280,20 @@
       assertVmStatus(vmURI, Status.POWERED_OFF);
    }
 
-   @Test(description = "POST /vApp/{id}/power/action/shutdown", dependsOnMethods = { "testDeployVm" })
+   @Test(description = "POST /vApp/{id}/power/action/shutdown", dependsOnMethods = { "testInstallVMwareTools" })
    public void testShutdown() {
       // Power on Vm
       vm = powerOnVm(vm.getHref());
 
       // The method under test
-      Task shutdown = vmApi.shutdown(vmURI);
+      Task shutdown = vmApi.shutdown(vm.getHref());
       assertTaskSucceedsLong(shutdown);
 
       // Get the updated Vm
-      vm = vmApi.getVm(vmURI);
+      vm = vmApi.getVm(vm.getHref());
 
       // Check status
-      assertVmStatus(vmURI, Status.POWERED_OFF);
-
-      // Power on the Vm again
-      vm = powerOnVm(vm.getHref());
+      assertVmStatus(vm.getHref(), Status.POWERED_OFF);
    }
 
    @Test(description = "POST /vApp/{id}/power/action/suspend", dependsOnMethods = { "testDeployVm" })
@@ -359,6 +356,7 @@
       vm = powerOnVm(vm.getHref());
       
       // The method under test
+      // NB this will put the vm in partially powered off state
       Task powerOffVm = vmApi.powerOff(vm.getHref());
       assertTrue(retryTaskSuccess.apply(powerOffVm), String.format(TASK_COMPLETE_TIMELY, "powerOffVm"));
 
@@ -391,8 +389,8 @@
 
    @Test(description = "POST /vApp/{id}/action/installVMwareTools", dependsOnMethods = { "testDeployVm" })
    public void testInstallVMwareTools() {
-      // First ensure the vApp is powered n
-      vm = powerOnVm(vm.getHref());
+      // First ensure the vApp is powered on
+     vm = powerOnVm(vm.getHref());
 
       // The method under test
       Task installVMwareTools = vmApi.installVMwareTools(vm.getHref());
@@ -420,6 +418,9 @@
       // The method under test
       Task upgradeHardwareVersion = vmApi.upgradeHardwareVersion(vm.getHref());
       assertTrue(retryTaskSuccess.apply(upgradeHardwareVersion), String.format(TASK_COMPLETE_TIMELY, "upgradeHardwareVersion"));
+      
+      // Power on the Vm again
+      vm = powerOnVm(vm.getHref());
    }
 
    @Test(description = "GET /vApp/{id}/guestCustomizationSection", dependsOnMethods = { "testGetVm" })
@@ -438,7 +439,7 @@
       GuestCustomizationSection oldSection = vmApi.getGuestCustomizationSection(vm.getHref());
       GuestCustomizationSection newSection = oldSection.toBuilder()
             .computerName(name("n"))
-            .enabled(Boolean.FALSE)
+            .enabled(Boolean.TRUE)
             .adminPassword(null) // Not allowed
             .build();
 
@@ -454,7 +455,7 @@
 
       // Check the modified section fields are set correctly
       assertEquals(modified.getComputerName(), newSection.getComputerName());
-      assertFalse(modified.isEnabled());
+      assertTrue(modified.isEnabled());
 
       // Reset the admin password in the retrieved GuestCustomizationSection for equality check
       modified = modified.toBuilder().adminPassword(null).build();
@@ -499,8 +500,9 @@
    }
 
    // FIXME "Task error: Unable to perform this action. Contact your cloud administrator."
-   @Test(description = "PUT /vApp/{id}/networkConnectionSection", dependsOnMethods = { "testGetNetworkConnectionSection" })
+   @Test(description = "PUT /vApp/{id}/networkConnectionSection", dependsOnMethods = { "testModifyGuestCustomizationSection" })
    public void testModifyNetworkConnectionSection() {
+      powerOffVm(vm.getHref());
       // Look up a network in the Vdc
       Set<Reference> networks = vdc.getAvailableNetworks();
       Reference network = Iterables.getLast(networks);
@@ -574,6 +576,7 @@
 
    @Test(description = "PUT /vApp/{id}/productSections", dependsOnMethods = { "testGetProductSections" })
    public void testModifyProductSections() {
+      powerOffVm(vm.getHref());
       // Copy existing section and update fields
       ProductSectionList oldSections = vmApi.getProductSections(vm.getHref());
       ProductSectionList newSections = oldSections.toBuilder()
@@ -600,7 +603,7 @@
       assertEquals(modified.getProductSections().size(), oldSections.getProductSections().size() + 1);
 
       // Check the section was modified correctly
-      assertEquals(modified, newSections, String.format(ENTITY_EQUAL, "ProductSectionList"));
+      assertEquals(modified, newSections);
    }
 
    // FIXME How do we force it to ask a question?
@@ -646,7 +649,7 @@
    }
 
    // FIXME If still failing, consider escalating?
-   @Test(description = "GET /vApp/{id}/screen", dependsOnMethods = { "testDeployVm" })
+   @Test(description = "GET /vApp/{id}/screen", dependsOnMethods = { "testInstallVMwareTools" })
    public void testGetScreenImage() {
       // Power on Vm
       vm = powerOnVm(vm.getHref());
@@ -933,18 +936,23 @@
       checkMetadata(metadata);
       
       // Check requirements for this test
-      assertFalse(Iterables.isEmpty(metadata.getMetadataEntries()), String.format(NOT_EMPTY_OBJECT_FMT, "MetadataEntry", "vm"));
+      assertTrue(Iterables.isEmpty(metadata.getMetadataEntries()), String.format(NOT_EMPTY_OBJECT_FMT, "MetadataEntry", "vm"));
    }
    
    @Test(description = "GET /vApp/{id}/metadata/{key}", dependsOnMethods = { "testGetMetadata" })
    public void testGetOrgMetadataValue() {
+      key = name("key-");
+      String value = name("value-");
+      metadataValue = MetadataValue.builder().value(value).build();
+      vmApi.getMetadataApi().setMetadata(vm.getHref(), key, metadataValue);
+      
       // Call the method being tested
-      MetadataValue value = vmApi.getMetadataApi().getMetadataValue(vm.getHref(), key);
+      MetadataValue metadataValue = vmApi.getMetadataApi().getMetadataValue(vm.getHref(), key);
       
       String expected = metadataValue.getValue();
 
-      checkMetadataValue(value);
-      assertEquals(value.getValue(), expected, String.format(CORRECT_VALUE_OBJECT_FMT, "Value", "MetadataValue", expected, value.getValue()));
+      checkMetadataValue(metadataValue);
+      assertEquals(metadataValue.getValue(), expected, String.format(CORRECT_VALUE_OBJECT_FMT, "Value", "MetadataValue", expected, metadataValue.getValue()));
    }
 
    @Test(description = "DELETE /vApp/{id}/metadata/{key}", dependsOnMethods = { "testSetMetadataValue" })
@@ -1002,11 +1010,17 @@
 
       // Get the updated VApp and the Vm
       delete = vAppApi.getVApp(delete.getHref());
-      Vm temp = Iterables.getOnlyElement(delete.getChildren().getVms());
+      List<Vm> vms = delete.getChildren().getVms();
+      Vm temp = Iterables.get(vms, 0);
 
-      // Power off the Vm
-      temp = powerOffVm(temp.getHref());
-
+      // otherwise it's impossible to stop a running vApp with no vms 
+      if(vms.size() == 1) {
+         UndeployVAppParams undeployParams = UndeployVAppParams.builder().build();
+         Task shutdownVapp = vAppApi.undeploy(delete.getHref(), undeployParams);
+         assertTaskSucceedsLong(shutdownVapp);
+      } else {
+         powerOffVm(temp.getHref());
+      }
       // The method under test
       Task deleteVm = vmApi.deleteVm(temp.getHref());
       assertTrue(retryTaskSuccess.apply(deleteVm), String.format(TASK_COMPLETE_TIMELY, "deleteVm"));
diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorApiLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorApiLiveTest.java
index 1c1a230..5ce92f6 100644
--- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorApiLiveTest.java
+++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorApiLiveTest.java
@@ -358,7 +358,7 @@
    }
 
    /** Build an {@link InstantiationParams} object. */
-   private InstantiationParams instantiationParams() {
+   protected InstantiationParams instantiationParams() {
       InstantiationParams instantiationParams = InstantiationParams.builder()
             .sections(ImmutableSet.of(networkConfigSection()))
             .build();
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NetworkAdapter.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NetworkAdapter.java
index 9b6c203..3294046 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NetworkAdapter.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NetworkAdapter.java
@@ -24,8 +24,7 @@
 import java.util.Collections;
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
+import org.jclouds.javax.annotation.Nullable;
 import org.virtualbox_4_1.NATProtocol;
 import org.virtualbox_4_1.NetworkAttachmentType;
 
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/ApplyMemoryToMachine.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/ApplyMemoryToMachine.java
index c40fedc..7dba773 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/ApplyMemoryToMachine.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/ApplyMemoryToMachine.java
@@ -19,8 +19,6 @@
 
 package org.jclouds.virtualbox.functions;
 
-import javax.annotation.Nullable;
-
 import org.virtualbox_4_1.IMachine;
 
 import com.google.common.base.Function;
@@ -37,7 +35,7 @@
    }
 
    @Override
-   public Object apply(@Nullable IMachine machine) {
+   public Object apply(IMachine machine) {
       machine.setMemorySize(memorySize);
       machine.saveSettings();
       return null;
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java
index f359689..323229f 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java
@@ -21,8 +21,6 @@
 import static org.virtualbox_4_1.NetworkAdapterType.Am79C973;
 import static org.virtualbox_4_1.NetworkAttachmentType.Bridged;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.INetworkAdapter;
@@ -42,7 +40,7 @@
    }
 
    @Override
-   public Void apply(@Nullable IMachine machine) {
+   public Void apply(IMachine machine) {
       INetworkAdapter iNetworkAdapter = machine.getNetworkAdapter(networkInterfaceCard.getSlot());
       iNetworkAdapter.setAttachmentType(Bridged);
       iNetworkAdapter.setAdapterType(Am79C973);
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachHostOnlyAdapter.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachHostOnlyAdapter.java
index 35ee8c9..016b942 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachHostOnlyAdapter.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachHostOnlyAdapter.java
@@ -21,8 +21,6 @@
 import static org.virtualbox_4_1.NetworkAdapterType.Am79C973;
 import static org.virtualbox_4_1.NetworkAttachmentType.HostOnly;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.INetworkAdapter;
@@ -41,7 +39,7 @@
    }
 
    @Override
-   public Void apply(@Nullable IMachine machine) {
+   public Void apply(IMachine machine) {
       INetworkAdapter iNetworkAdapter = machine.getNetworkAdapter(networkInterfaceCard.getSlot());
       iNetworkAdapter.setAttachmentType(HostOnly);
       iNetworkAdapter.setAdapterType(Am79C973);
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachMediumToMachineIfNotAlreadyAttached.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachMediumToMachineIfNotAlreadyAttached.java
index da90d1c..3a5f3ab 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachMediumToMachineIfNotAlreadyAttached.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachMediumToMachineIfNotAlreadyAttached.java
@@ -19,8 +19,6 @@
 
 package org.jclouds.virtualbox.functions;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.virtualbox.domain.DeviceDetails;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.IMedium;
@@ -44,7 +42,7 @@
    }
 
    @Override
-   public Void apply(@Nullable IMachine machine) {
+   public Void apply(IMachine machine) {
       try {
          machine.attachDevice(controllerName, device.getPort(), device.getDeviceSlot(), device.getDeviceType(), medium);
          machine.saveSettings();
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java
index 511b0af..9dddd71 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java
@@ -21,8 +21,6 @@
 
 import static org.virtualbox_4_1.NetworkAttachmentType.NAT;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
 import org.jclouds.virtualbox.domain.RedirectRule;
 import org.virtualbox_4_1.IMachine;
@@ -43,7 +41,7 @@
    }
 
    @Override
-   public Void apply(@Nullable IMachine machine) {
+   public Void apply(IMachine machine) {
       INetworkAdapter iNetworkAdapter = machine.getNetworkAdapter(networkInterfaceCard.getSlot());
       iNetworkAdapter.setAttachmentType(NAT);
       for (RedirectRule rule : networkInterfaceCard.getNetworkAdapter().getRedirectRules()) {
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNicToMachine.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNicToMachine.java
index 586bc41..f24f5b0 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNicToMachine.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNicToMachine.java
@@ -20,8 +20,6 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
 import org.jclouds.virtualbox.util.MachineUtils;
 import org.virtualbox_4_1.NetworkAttachmentType;
@@ -42,7 +40,7 @@
    }
 
    @Override
-   public Void apply(@Nullable NetworkInterfaceCard nic) {
+   public Void apply(NetworkInterfaceCard nic) {
       if (hasNatAdapter(nic)) {
          return machineUtils.writeLockMachineAndApply(vmName, new AttachNATAdapterToMachineIfNotAlreadyExists(nic));
       } else if (hasBridgedAdapter(nic)) {
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java
index 66a6e38..fb8dc97 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java
@@ -25,7 +25,6 @@
 import java.io.File;
 import java.util.Set;
 
-import javax.annotation.Nullable;
 import javax.annotation.Resource;
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -78,7 +77,7 @@
    }
 
    @Override
-   public IMachine apply(@Nullable MasterSpec launchSpecification) {
+   public IMachine apply(MasterSpec launchSpecification) {
       final IVirtualBox vBox = manager.get().getVBox();
       String vmName = launchSpecification.getVmSpec().getVmName();
       String vmId = launchSpecification.getVmSpec().getVmId();
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExists.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExists.java
index c617666..7aacd94 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExists.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateMediumIfNotAlreadyExists.java
@@ -22,7 +22,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
@@ -62,7 +61,7 @@
    public static final Pattern ATTACHED_PATTERN = Pattern.compile(".*is still attached.*: ([-0-9a-f]+) .*");
 
    @Override
-   public IMedium apply(@Nullable HardDisk hardDisk) {
+   public IMedium apply(HardDisk hardDisk) {
       IVirtualBox vBox = manager.get().getVBox();
       try {
          String diskPath = hardDisk.getDiskPath();
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/DetachDistroMediumFromMachine.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/DetachDistroMediumFromMachine.java
index 7f744e1..ad1246e 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/DetachDistroMediumFromMachine.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/DetachDistroMediumFromMachine.java
@@ -18,8 +18,6 @@
  */
 package org.jclouds.virtualbox.functions;
 
-import javax.annotation.Nullable;
-
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.VBoxException;
 
@@ -42,7 +40,7 @@
 
    //TODO: should this be a function on HardDisk?
    @Override
-   public Void apply(@Nullable IMachine machine) {
+   public Void apply(IMachine machine) {
       try {
          machine.detachDevice(controller, port, deviceSlot);
          machine.saveSettings();
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToIpAddress.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToIpAddress.java
index 0e47da2..f7a97e5 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToIpAddress.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToIpAddress.java
@@ -21,8 +21,6 @@
 
 import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.compute.options.RunScriptOptions;
@@ -49,7 +47,7 @@
    }
 
    @Override
-   public String apply(@Nullable IMachine machine) {
+   public String apply(IMachine machine) {
 
       String macAddress = machine.getNetworkAdapter(0l).getMACAddress();
       int offset = 0, step = 2;
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToVmSpec.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToVmSpec.java
index c4be85e..a3344dc 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToVmSpec.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToVmSpec.java
@@ -21,7 +21,6 @@
 
 import java.util.List;
 
-import javax.annotation.Nullable;
 import javax.annotation.Resource;
 import javax.inject.Named;
 
@@ -53,7 +52,7 @@
    protected Logger logger = Logger.NULL;
 
    @Override
-   public VmSpec apply(@Nullable IMachine machine) {
+   public VmSpec apply(IMachine machine) {
       List<StorageController> controllers = buildControllers(machine);
 
       // TODO some parameters are predefined cause the IMachine doesn't have the
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java
index 9c9ee59..d644537 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java
@@ -20,7 +20,6 @@
 
 import static com.google.common.base.Throwables.propagate;
 
-import javax.annotation.Nullable;
 import javax.annotation.Resource;
 import javax.inject.Named;
 
@@ -70,7 +69,7 @@
    }
 
    @Override
-   public ISession apply(@Nullable IMachine machine) {
+   public ISession apply(IMachine machine) {
       ISession session = manager.getSessionObject();
       try {
          final IProgress progress = machine
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java
index eb3f259..218bd66 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java
@@ -19,8 +19,6 @@
 
 package org.jclouds.virtualbox.functions;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.logging.Logger;
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.IProgress;
@@ -52,7 +50,7 @@
    }
 
    @Override
-   public ISnapshot apply(@Nullable IMachine machine) {
+   public ISnapshot apply(IMachine machine) {
       // Snapshot a machine
       ISession session = null;
       ISnapshot snap = machine.getCurrentSnapshot();
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 d75b83d..4b5eeba 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
@@ -25,7 +25,6 @@
 
 import java.net.URI;
 
-import javax.annotation.Nullable;
 import javax.annotation.Resource;
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -36,8 +35,6 @@
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.location.Provider;
 import org.jclouds.logging.Logger;
-import org.jclouds.rest.annotations.Credential;
-import org.jclouds.rest.annotations.Identity;
 import org.jclouds.scriptbuilder.domain.Statements;
 import org.jclouds.virtualbox.predicates.RetryIfSocketNotYetOpen;
 import org.virtualbox_4_1.SessionState;
@@ -65,8 +62,7 @@
    @Inject
    public StartVBoxIfNotAlreadyRunning(Function<Supplier<NodeMetadata>, VirtualBoxManager> managerForNode,
             Factory runScriptOnNodeFactory, RetryIfSocketNotYetOpen socketTester, Supplier<NodeMetadata> host,
-            @Provider Supplier<URI> providerSupplier, @Nullable @Identity String identity,
-            @Nullable @Credential String credential) {
+            @Provider Supplier<URI> providerSupplier) {
       this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory");
       this.socketTester = checkNotNull(socketTester, "socketTester");
       this.socketTester.seconds(3L);
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/IMachinePredicates.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/IMachinePredicates.java
index 9c62b31..fad6968 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/IMachinePredicates.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/IMachinePredicates.java
@@ -27,8 +27,6 @@
 import static org.jclouds.virtualbox.util.IMediumAttachments.toMedium;
 import static org.virtualbox_4_1.DeviceType.HardDisk;
 
-import javax.annotation.Nullable;
-
 import org.virtualbox_4_1.IMachine;
 import org.virtualbox_4_1.IMedium;
 
@@ -48,7 +46,7 @@
 
       @SuppressWarnings("unchecked")
       @Override
-      public boolean apply(@Nullable IMachine machine) {
+      public boolean apply(IMachine machine) {
          Iterable<IMedium> mediumsOnMachine = transform(machine.getMediumAttachments(), toMedium());
          // TODO: previous impl walked the parent medium back to the child
          // before checking machine ids. Determine if that extra walk was really
diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java
index 418b7dc..0c8064d 100644
--- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java
+++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshAvailable.java
@@ -20,7 +20,6 @@
 
 import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript;
 
-import javax.annotation.Nullable;
 import javax.annotation.Resource;
 import javax.inject.Named;
 
@@ -48,7 +47,7 @@
    }
 
    @Override
-   public boolean apply(@Nullable String nodeId) {
+   public boolean apply(String nodeId) {
       boolean sshDaemonIsRunning = false;
       try {
          if (context.getComputeService()
diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java
index 2c5aae5..b3b31da 100644
--- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java
+++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunningLiveTest.java
@@ -56,8 +56,6 @@
       RetryIfSocketNotYetOpen client = createMock(RetryIfSocketNotYetOpen.class);
       NodeMetadata host = new NodeMetadataBuilder().id("host").status(Status.RUNNING).build();
       URI provider = URI.create("http://localhost:18083/");
-      String identity = "adminstrator";
-      String credential = "12345";
       expect(client.seconds(3)).andReturn(client);
       expect(client.apply(HostAndPort.fromParts(provider.getHost(), provider.getPort()))).andReturn(true).anyTimes();
       manager.connect(provider.toASCIIString(), "", "");
@@ -66,7 +64,7 @@
       replay(manager, runScriptOnNodeFactory, client);
 
       new StartVBoxIfNotAlreadyRunning((Function) Functions.constant(manager), runScriptOnNodeFactory, client,
-               Suppliers.ofInstance(host), Suppliers.ofInstance(provider), identity, credential).start();
+               Suppliers.ofInstance(host), Suppliers.ofInstance(provider)).start();
 
       verify(manager, runScriptOnNodeFactory, client);
 
@@ -83,8 +81,6 @@
       NodeMetadata host = new NodeMetadataBuilder().id("host").status(Status.RUNNING).operatingSystem(
                OperatingSystem.builder().description("unix").build()).build();
       URI provider = URI.create("http://localhost:18083/");
-      String identity = "adminstrator";
-      String credential = "12345";
       
       expect(client.seconds(3)).andReturn(client);
       expect(client.apply(HostAndPort.fromParts(provider.getHost(), provider.getPort()))).andReturn(false).once().andReturn(true).once();
@@ -106,7 +102,7 @@
 
       replay(manager, runScriptOnNodeFactory, runScriptOnNode, client);
       new StartVBoxIfNotAlreadyRunning((Function) Functions.constant(manager), runScriptOnNodeFactory, client,
-               Suppliers.ofInstance(host), Suppliers.ofInstance(provider), identity, credential);
+               Suppliers.ofInstance(host), Suppliers.ofInstance(provider));
       verify(manager, runScriptOnNodeFactory, runScriptOnNode, client);
 
    }
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/GitRepoAndRef.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/GitRepoAndRef.java
new file mode 100644
index 0000000..c54cfb3
--- /dev/null
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/GitRepoAndRef.java
@@ -0,0 +1,156 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.scriptbuilder.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+
+/**
+ * Description of git coordinates to checkout.
+ * 
+ * @author Adrian Cole
+ */
+public class GitRepoAndRef  {
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public Builder toBuilder() {
+      return new Builder().fromGitRepoAndRef(this);
+   }
+
+   public static class Builder {
+
+      protected URI repository;
+      protected Optional<String> branch = Optional.absent();
+      protected Optional<String> tag = Optional.absent();
+
+      /**
+       * @see GitRepoAndRef#getRepository()
+       */
+      public Builder repository(URI repository) {
+         this.repository = repository;
+         return this;
+      }
+
+      /**
+       * @see GitRepoAndRef#getRepository()
+       */
+      public Builder repository(String repository) {
+         return repository(URI.create(repository));
+      }
+
+      /**
+       * @see GitRepoAndRef#getBranch()
+       */
+      public Builder branch(String branch) {
+         this.branch = Optional.fromNullable(branch);
+         return this;
+      }
+
+      /**
+       * @see GitRepoAndRef#getTag()
+       */
+      public Builder tag(String tag) {
+         this.tag = Optional.fromNullable(tag);
+         return this;
+      }
+
+      public GitRepoAndRef build() {
+         return new GitRepoAndRef(repository, branch, tag);
+      }
+
+      public Builder fromGitRepoAndRef(GitRepoAndRef in) {
+         return this.repository(in.getRepository()).branch(in.getBranch().orNull()).tag(in.getTag().orNull());
+      }
+   }
+
+   protected final URI repository;
+   protected final Optional<String> branch;
+   protected final Optional<String> tag;
+
+   protected GitRepoAndRef(URI repository, Optional<String> branch, Optional<String> tag) {
+      this.repository = checkNotNull(repository, "repository");
+      this.branch = checkNotNull(branch, "branch");
+      this.tag = checkNotNull(tag, "tag");
+   }
+
+   /**
+    * The (possibly remote) repository to clone from.
+    */
+   public URI getRepository() {
+      return repository;
+   }
+
+   /**
+    * Instead of pointing the newly created HEAD to the branch pointed to by the cloned repository's
+    * HEAD, point to this branch instead. In a non-bare repository, this is the branch that will be
+    * checked out.
+    */
+   public Optional<String> getBranch() {
+      return branch;
+   }
+
+   /**
+    * checkout the following tag on the branch
+    */
+   public Optional<String> getTag() {
+      return tag;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(repository, branch, tag);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      GitRepoAndRef other = GitRepoAndRef.class.cast(obj);
+      return Objects.equal(this.repository, other.repository) && Objects.equal(this.branch, other.branch)
+               && Objects.equal(this.tag, other.tag);
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("repository", repository).add("branch", branch.orNull())
+               .add("tag", tag.orNull()).toString();
+   }
+
+}
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java
index bfc6df4..ea63c9a 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java
@@ -171,6 +171,26 @@
       return new PipeHttpResponseToTarxpzfIntoDirectory(method, endpoint, headers, directory);
    }
 
+   /**
+    * like {@link #extractTargzIntoDirectory(URI, String)} except that it
+    * flattens the first directory in the archive
+    * 
+    * For example, {@code apache-maven-3.0.4-bin.tar.gz} normally extracts
+    * directories like {@code ./apache-maven-3.0.4/bin}. This command eliminates
+    * the intermediate directory, in the example {@code ./apache-maven-3.0.4/}
+    * 
+    * @param tgz remote ref to download
+    * @param dest path where the files in the intermediate directory will end
+    */
+   public static Statement extractTargzAndFlattenIntoDirectory(URI tgz, String dest) {
+      return new StatementList(ImmutableSet.<Statement> builder()
+            .add(exec("mkdir /tmp/$$"))
+            .add(extractTargzIntoDirectory(tgz, "/tmp/$$"))
+            .add(exec("mkdir -p " + dest))
+            .add(exec("mv /tmp/$$/*/* " + dest))
+            .add(exec("rm -rf /tmp/$$")).build());
+   }
+   
    public static Statement extractTargzIntoDirectory(URI targz, String directory) {
       return extractTargzIntoDirectory("GET", targz, ImmutableMultimap.<String, String> of(), directory);
    }
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/osgi/ServiceFunctionLoader.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/osgi/ServiceFunctionLoader.java
index b4a6ce6..584b1ea 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/osgi/ServiceFunctionLoader.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/functionloader/osgi/ServiceFunctionLoader.java
@@ -55,13 +55,15 @@
       String filter = String.format("(function=*%s.%s*)", function, ShellToken.SH.to(family));
       try {
          references = bundleContext.getServiceReferences(FunctionLoader.class.getName(), filter);
-         for (ServiceReference reference : references) {
+        if (references != null) {
+          for (ServiceReference reference : references) {
             FunctionLoader loader = (FunctionLoader) bundleContext.getService(reference);
             String f = loader.loadFunction(function, family);
             if (!Strings.isNullOrEmpty(f)) {
-               return f;
+              return f;
             }
-         }
+          }
+        }
       } catch (InvalidSyntaxException e) {
          throw new FunctionNotFoundException(function, family, e);
       } finally {
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/git/CloneGitRepo.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/git/CloneGitRepo.java
index 2d555ab..9b3fe93 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/git/CloneGitRepo.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/git/CloneGitRepo.java
@@ -22,6 +22,7 @@
 
 import java.net.URI;
 
+import org.jclouds.scriptbuilder.domain.GitRepoAndRef;
 import org.jclouds.scriptbuilder.domain.OsFamily;
 import org.jclouds.scriptbuilder.domain.Statement;
 import org.jclouds.scriptbuilder.domain.Statements;
@@ -33,9 +34,10 @@
 import com.google.common.collect.Iterables;
 
 /**
- * Clones a repository into a newly created directory, creates remote-tracking branches for each
- * branch in the cloned repository (visible using git branch -r), and creates and checks out an
- * initial branch that is forked from the cloned repository's currently active branch. PWD is set to
+ * Clones a gitRepoAndRef into a newly created directory, creates
+ * remote-tracking branches for each branch in the cloned gitRepoAndRef (visible
+ * using git branch -r), and creates and checks out an initial branch that is
+ * forked from the cloned gitRepoAndRef's currently active branch. PWD is set to
  * the directory being checked out.
  * 
  * @author Adrian Cole
@@ -52,39 +54,45 @@
 
    public static class Builder {
 
-      protected URI repository;
-      protected Optional<String> branch = Optional.absent();
-      protected Optional<String> tag = Optional.absent();
+      protected GitRepoAndRef.Builder gitRepoAndRef = GitRepoAndRef.builder();
       protected Optional<String> directory = Optional.absent();
 
       /**
-       * @see CloneGitRepo#getRepository()
+       * @see GitRepoAndRef#getRepository()
        */
       public Builder repository(URI repository) {
-         this.repository = repository;
+         this.gitRepoAndRef.repository(repository);
          return this;
       }
 
       /**
-       * @see CloneGitRepo#getRepository()
+       * @see GitRepoAndRef#getRepository()
        */
       public Builder repository(String repository) {
          return repository(URI.create(repository));
       }
 
       /**
-       * @see CloneGitRepo#getBranch()
+       * @see GitRepoAndRef#getBranch()
        */
       public Builder branch(String branch) {
-         this.branch = Optional.fromNullable(branch);
+         this.gitRepoAndRef.branch(branch);
          return this;
       }
 
       /**
-       * @see CloneGitRepo#getTag()
+       * @see GitRepoAndRef#getTag()
        */
       public Builder tag(String tag) {
-         this.tag = Optional.fromNullable(tag);
+         this.gitRepoAndRef.tag(tag);
+         return this;
+      }
+
+      /**
+       * @see CloneGitRepo#getGitRepoAndRef()
+       */
+      public Builder gitRepoAndRef(GitRepoAndRef gitRepoAndRef) {
+         this.gitRepoAndRef.fromGitRepoAndRef(gitRepoAndRef);
          return this;
       }
 
@@ -97,54 +105,33 @@
       }
 
       public CloneGitRepo build() {
-         return new CloneGitRepo(repository, branch, tag, directory);
+         return new CloneGitRepo(gitRepoAndRef.build(), directory);
       }
 
       public Builder fromCloneGitRepo(CloneGitRepo in) {
-         return this.repository(in.getRepository()).branch(in.getBranch().orNull()).tag(in.getTag().orNull())
-                  .directory(in.getDirectory().orNull());
+         return this.gitRepoAndRef(in.getGitRepoAndRef()).directory(in.getDirectory().orNull());
       }
    }
 
-   protected final URI repository;
-   protected final Optional<String> branch;
-   protected final Optional<String> tag;
+   protected final GitRepoAndRef gitRepoAndRef;
    protected final Optional<String> directory;
 
-   protected CloneGitRepo(URI repository, Optional<String> branch, Optional<String> tag, Optional<String> directory) {
-      this.repository = checkNotNull(repository, "repository");
-      this.branch = checkNotNull(branch, "branch");
-      this.tag = checkNotNull(tag, "tag");
+   protected CloneGitRepo(GitRepoAndRef gitRepoAndRef, Optional<String> directory) {
+      this.gitRepoAndRef = checkNotNull(gitRepoAndRef, "gitRepoAndRef");
       this.directory = checkNotNull(directory, "directory");
    }
 
    /**
-    * The (possibly remote) repository to clone from.
+    * The coordinates to checkout
     */
-   public URI getRepository() {
-      return repository;
+   public GitRepoAndRef getGitRepoAndRef() {
+      return gitRepoAndRef;
    }
-
+   
    /**
-    * Instead of pointing the newly created HEAD to the branch pointed to by the cloned repository's
-    * HEAD, point to this branch instead. In a non-bare repository, this is the branch that will be
-    * checked out.
-    */
-   public Optional<String> getBranch() {
-      return branch;
-   }
-
-   /**
-    * checkout the following tag on the branch
-    */
-   public Optional<String> getTag() {
-      return tag;
-   }
-
-   /**
-    * The name of a new directory to clone into. The "humanish" part of the source repository is
-    * used if no directory is explicitly given (repo for /path/to/repo.git and foo for
-    * host.xz:foo/.git).
+    * The name of a new directory to clone into. The "humanish" part of the
+    * source gitRepoAndRef is used if no directory is explicitly given (repo for
+    * /path/to/repo.git and foo for host.xz:foo/.git).
     */
    public Optional<String> getDirectory() {
       return directory;
@@ -155,7 +142,7 @@
     */
    @Override
    public int hashCode() {
-      return Objects.hashCode(repository, branch, tag, directory);
+      return Objects.hashCode(gitRepoAndRef, directory);
    }
 
    /**
@@ -170,8 +157,7 @@
       if (getClass() != obj.getClass())
          return false;
       CloneGitRepo other = CloneGitRepo.class.cast(obj);
-      return Objects.equal(this.repository, other.repository) && Objects.equal(this.branch, other.branch)
-               && Objects.equal(this.tag, other.tag) && Objects.equal(this.directory, other.directory);
+      return Objects.equal(this.gitRepoAndRef, other.gitRepoAndRef) && Objects.equal(this.directory, other.directory);
    }
 
    /**
@@ -189,16 +175,16 @@
    public String render(OsFamily arg0) {
       StringBuilder command = new StringBuilder();
       command.append("git clone");
-      if (branch.isPresent())
-         command.append(" -b ").append(branch.get());
-      command.append(' ').append(repository.toASCIIString());
+      if (gitRepoAndRef.getBranch().isPresent())
+         command.append(" -b ").append(gitRepoAndRef.getBranch().get());
+      command.append(' ').append(gitRepoAndRef.getRepository().toASCIIString());
       if (directory.isPresent())
          command.append(' ').append(directory.get());
       command.append("{lf}");
       command.append("{cd} ").append(
-               directory.or(Iterables.getLast(Splitter.on('/').split(repository.getPath())).replace(".git", "")));
-      if (tag.isPresent()) {
-         command.append("{lf}").append("git checkout ").append(tag.get());
+            directory.or(Iterables.getLast(Splitter.on('/').split(gitRepoAndRef.getRepository().getPath())).replace(".git", "")));
+      if (gitRepoAndRef.getTag().isPresent()) {
+         command.append("{lf}").append("git checkout ").append(gitRepoAndRef.getTag().get());
       }
       return Statements.exec(command.toString()).render(arg0);
    }
@@ -208,8 +194,8 @@
     */
    @Override
    public String toString() {
-      return Objects.toStringHelper(this).omitNullValues().add("repository", repository).add("branch", branch.orNull())
-               .add("tag", tag.orNull()).add("directory", directory.orNull()).toString();
+      return Objects.toStringHelper(this).omitNullValues().add("gitRepoAndRef", gitRepoAndRef)
+            .add("directory", directory.orNull()).toString();
    }
 
 }
diff --git a/scriptbuilder/src/main/resources/functions/installGit.sh b/scriptbuilder/src/main/resources/functions/installGit.sh
index a4a6697..cb72f29 100644
--- a/scriptbuilder/src/main/resources/functions/installGit.sh
+++ b/scriptbuilder/src/main/resources/functions/installGit.sh
@@ -2,6 +2,16 @@
   if which dpkg &> /dev/null; then
     ensure_cmd_or_install_package_apt git git-core
   elif which rpm &> /dev/null; then
+    case $(uname -r) in
+      *el5)
+        wget http://download.fedoraproject.org/pub/epel/5/$(uname -i)/epel-release-5-4.noarch.rpm &&
+        rpm -Uvh epel-release-5-4.noarch.rpm
+        rm -f epel-release-5-4.noarch.rpm;;
+      *el6)
+        wget http://download.fedoraproject.org/pub/epel/6/$(uname -i)/epel-release-6-7.noarch.rpm &&
+        rpm -Uvh epel-release-6-7.noarch.rpm 
+        rm -f epel-release-6-7.noarch.rpm;; 
+    esac
     ensure_cmd_or_install_package_yum git git-core
   else
     abort "we only support apt-get and yum right now... please contribute!"
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/StatementsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/StatementsTest.java
index af30767..c44aa21 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/StatementsTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/StatementsTest.java
@@ -48,5 +48,20 @@
             save.render(OsFamily.UNIX),
             "curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET  https://s3.amazonaws.com/MinecraftDownload/launcher/minecraft_server.tar.gz |(mkdir -p /opt/minecraft &&cd /opt/minecraft &&tar -xpzf -)\n");
    }
+   
+   public void testExtractTargzAndFlattenIntoDirectoryUNIX() {
+      Statement save = Statements
+            .extractTargzAndFlattenIntoDirectory(
+                  URI.create("http://www.us.apache.org/dist/maven/binaries/apache-maven-3.0.4-bin.tar.gz"),
+                  "/usr/local/maven");
+      assertEquals(
+            save.render(OsFamily.UNIX),
+            "mkdir /tmp/$$\n" +
+            "curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET  http://www.us.apache.org/dist/maven/binaries/apache-maven-3.0.4-bin.tar.gz |(mkdir -p /tmp/$$ &&cd /tmp/$$ &&tar -xpzf -)\n" +
+            "mkdir -p /usr/local/maven\n" +
+            "mv /tmp/$$/*/* /usr/local/maven\n" +
+            "rm -rf /tmp/$$\n");
+   }
+
 
 }
diff --git a/scriptbuilder/src/test/resources/test_install_git_scriptbuilder.sh b/scriptbuilder/src/test/resources/test_install_git_scriptbuilder.sh
index 7e40e47..d942959 100644
--- a/scriptbuilder/src/test/resources/test_install_git_scriptbuilder.sh
+++ b/scriptbuilder/src/test/resources/test_install_git_scriptbuilder.sh
@@ -138,6 +138,16 @@
   if which dpkg &> /dev/null; then
     ensure_cmd_or_install_package_apt git git-core
   elif which rpm &> /dev/null; then
+    case $(uname -r) in
+      *el5)
+        wget http://download.fedoraproject.org/pub/epel/5/$(uname -i)/epel-release-5-4.noarch.rpm &&
+        rpm -Uvh epel-release-5-4.noarch.rpm
+        rm -f epel-release-5-4.noarch.rpm;;
+      *el6)
+        wget http://download.fedoraproject.org/pub/epel/6/$(uname -i)/epel-release-6-7.noarch.rpm &&
+        rpm -Uvh epel-release-6-7.noarch.rpm 
+        rm -f epel-release-6-7.noarch.rpm;; 
+    esac
     ensure_cmd_or_install_package_yum git git-core
   else
     abort "we only support apt-get and yum right now... please contribute!"